# CAXperts Help Portal — Full Documentation

> Product documentation and help for CAXperts products. Each link points to a documentation page; known AI/bot user-agents receive the raw Markdown directly.

# UDiTH — Introduction

# UDiTH

The UDiTH infrastructure consists of the following components:

- [UDiTH App](https://help.caxperts.com/UDiTH%20App/), an application for end users.
- [UDiTH Capture Station](https://help.caxperts.com/UDiTH%20Capture%20Station/), a collection of software tools used to create UDiTH models.
- [UDiTH Portal](https://help.caxperts.com/UDiTH%20Portal/), a collection of software tools used to host UDiTH models and to provide a web-based interface for accessing them.

---

# UDiTH — Moving from UPV to UDiTH

# Moving from UPV to UDiTH

## Step 1: Prerequisites for the Evaluation and Installation of UDiTH
- Use a dedicated computer on which **UniversalPlantViewer**, **UniversalPlantViewer Builder**, and **Adapter** are **not** installed.  
- If UPV models already exist, ensure that this computer has appropriate access to a UPV model for initial testing.  

## Step 2: Accessing an Existing UPV Model Using UDiTH
- Install the **UDiTH Viewer**.  
- Create the licence file **`udithlicense.txt`**, containing only the UDiTH licence key provided by **CAXperts**.  
- Copy this file into the **`\Data`** directory of the relevant UPV model.  
- **Access test:** Launch the UDiTH Viewer and open the UPV model using the newly added UDiTH licence key to confirm that it is working correctly.  

## Step 3: Updating All UPV Models and Workstations to the UDiTH Viewer
- Add the UDiTH licence file **`udithlicense.txt`** to all centrally and locally stored UPV models.  
- Uninstall **UniversalPlantViewer** from all workstations.  
- Install the **UDiTH Viewer** on all workstations to replace the previous application.  

## Step 4: UDiTH Capture Station – Creation of UDiTH Models
- Install the **Adapters** and **UDiTH Builder** version **2026.X**.  
- Open either an Adapter or **UDiTH Builder**, and activate the UDiTH licence via the *Help → Update Licence* dialogue.  
- Carry out an initial data conversion test using an Adapter for 3D data (for example **AdapterForE3D**, **Ifc**, **LaserScans**, **Navisworks**, or **SmartReview**).  
- Perform an initial model creation test using the **UDiTH Builder**.  
- Open the generated UDiTH model with the **UDiTH Viewer** to verify successful creation and compatibility.  

## Step 5: Scripts (Command Line Mode) for Model Creation
- Update all model creation scripts to reflect the installation of **UDiTH Builder** and its revised installation directory structure.  

## Step 6: Updating Additional UDiTH Capture Stations for Model Generation
- **Adapters (version 2026.X)** and **UDiTH Builder (version 2026.X)** must **not** be installed alongside Adapters or **UniversalPlantViewer Builder** versions **6.X** or **7.X**.  
- Before installation, uninstall all Adapters and **UniversalPlantViewer Builder** versions **6.X** or **7.X**.  
- Install the **Adapters** and **UDiTH Builder** version **2026.X**.  
- Activate the UDiTH licence using either the Adapter or Builder via the *Help → Update Licence* dialogue.  

## Step 7: Migration of UPV Web Services to the UDiTH Portal
For detailed migration instructions, please refer to the **UDiTH Portal Migration Guide**, available at:  
[https://help.caxperts.com/UDiTH%20Portal/Migration%20Guide](https://help.caxperts.com/UDiTH%20Portal/Migration%20Guide)

---

# UDiTH App — Introduction

# Introduction

**UDiTH** is an easy-to-use tool that brings digital twins of your models, such as chemical plants, ships, offshore platforms, and buildings, to any device running Windows, iOS, Android, or a web browser.
The digital twin can be used both online and offline. In **UDiTH**, you can link internal and external specifications, manuals, isometrics, and other documentation to your 3D model, helping you stay up to date with the status of individual components.

## Technology

-   Based on the Unity game engine and available for Windows, iOS, and Android
-   Supports streaming and local storage

## Prerequisites

To use **UDiTH**, you need one of the following:

-   Windows
-   Android
-   iOS
-   A web browser

> If the model is used in streaming format, a large amount of data may be transferred. On mobile devices, the model size may be limited by the available memory.

### Windows
- OS: Windows 10 or later
- OS: Windows Server 2019 or later
- RAM: 8 GB or more
- Graphics: On-board graphics, dedicated graphics card with 4 GB recommended

### iOS
- OS: iOS 18 (recommended iOS 26)
- RAM: 4 GB (recommended 16 GB)

### Android
- OS: Android 14 (Android 17 recommended)
- RAM: 8 GB (12 GB recommended)

(usage may be restricted by the device manufacturer)

### Web Browser

Any up-to-date web browser is compatible.

## Supported Formats

**UDiTH** uses input files created by **UDiTH Builder**. Please refer to the **[UDiTH Builder](https://help.caxperts.com/UDiTH%20Capture%20Station/Builder)** manual for a full list of supported 2D and 3D input formats.

---

# UDiTH App — Setup

# Setup

## Installing the UDiTH App

### Windows

- Run the `.exe` created by **UDiTH Builder**.
- Alternatively, download and run the `.exe` from the [CAXperts download page](https://downloads.caxperts.com).

> UDiTH for Windows is provided as a small `.exe` file that does not require installation or administrator rights.

### Android

- Download and install UDiTH from the [Google Play Store](https://play.google.com/store/apps/details?id=com.caxperts.udith).

### iOS

- Download and install UDiTH from the Apple App Store.

### Web Browser

- For UDiTH [BrowserBasedViewing](https://help.caxperts.com/WebServices%20Server/#UDiTH-webservices-overview) (BBV), no installation is required. Simply open the page in your web browser.

## Uninstalling the UDiTH App

To remove the application, either delete the `.exe` file from your computer or follow the uninstall instructions provided for your mobile device.

---

# UDiTH App — What's new

# What's new?

> UniversalPlantViewer has been rebranded as **UDiTH**. This change also applies to the names of the **.exe** files. Please update any **batch files** accordingly to ensure continued compatibility with the new name.

- UDiTH now supports multi-window functionality, allowing you to view a P&ID drawing alongside the 3D window at the same time.
- The file menu button now only toggles the display of the **Files** tab.
- All **Save** and **Load** buttons have been moved to the **Files** tab.
- The option to toggle **Red Bubbles** for 360-degree panoramas is now available from the camera menu.

---

# UDiTH App — Start Window

# Start Window

After starting **UDiTH**, you will see a list of projects together with additional information about them.

<img src="./media/image282.png" width="400" height="242">

1.  Language selection drop-down

2.  Project options

3.  Project file path

4.  Folder navigation

## Loading Models

To load a valid model, either click it in the list or enter a **file path** and click the **Stream** button in the lower-right corner.

Below is a list of the available interactive icons and their functions.

<table>
<tr><th>Button</th><th>Action</th></tr>
<tr><td>

<img src="./media/ButtonOpenLogs.png" width="44" height="44">

</td><td>

Shows the list of log files. You can open a log file and copy its contents to the clipboard.

</td></tr>
<tr><td>

<img src="./media/ButtonInvalidModel.png"  width="44" height="44">

</td><td>

The local model is invalid and can no longer be accessed.

</td></tr>
<tr><td>

<img src="./media/ButtonOutdatedModel.png"  width="44" height="44">

</td><td>

This local version is outdated and can no longer be accessed. You can set the expiration date of a model during model generation in **UDiTH Builder**.

</td></tr>
<tr><td>

<img src="./media/ButtonStream.png"  width="44" height="44">

</td><td>

Streams a model from the internet.

</td></tr>
<tr><td>

<img src="./media/ButtonDownload.png"  width="44" height="44">

</td><td>

Downloads a model to the local device, for example when taking a mobile device to a plant without an internet connection.

</td></tr>
<tr><td>

<img src="./media/ButtonQR.png"  width="44" height="44">

</td><td>

Opens the camera to scan a QR code.

</td></tr>
<tr><td>

<img src="./media/ButtonCopy2.png"  width="44" height="44">

</td><td>

Copies the file name to the input box.

</td></tr>
<tr><td>

<img src="./media/ButtonDelete.png"  width="44" height="44">

</td><td>

Deletes the local version.

</td></tr>
</table>

You can enter a custom file path, either local or online, in the field at the bottom of the screen and then stream or download the model.

<img src="./media/image19.png"  width="400" height="90">

> Please use UDiTH Webservices if you want to protect UDiTH models. Since UDiTH 7, Basic Authentication is no longer supported.

You can also **allow downloads** using the options provided in the **Generate output** settings of **UDiTH Builder**.

### Close Window

> If you click **Close** in the top-right corner or press **Alt + F4**, a confirmation message will appear to help prevent accidental closure of the program or loss of data.

<img src="./media/image21.png" width="223" height="113">

---

# UDiTH App — Multi-Window System

# Multi-Window System

The tabs within **UDiTH** can be customised using drag and drop. The **Search**, **Files**, **Attributes**, and many other tabs can be repositioned easily by clicking and dragging them across the screen. The image below shows two possible layout options after moving a tab to a new position.

> This option must be activated by an administrator.

<img src="./media/image284.png" width="698" height="368">

---

# UDiTH App — Navigation

# Navigation

On touch devices, you can navigate like this:

### Two-finger parallel drag: pan

Place two fingers on the 3D view and move them in approximately the same direction. The camera pans by the average movement of both fingers.

Recognition requires both fingers to move more than 10 screen units and the angle between their movement vectors to be less than 90 degrees. When **Swap pan and rotate** is enabled, this gesture rotates instead.

### Pinch or spread: zoom

Move two fingers toward or away from each other to zoom around their midpoint.

- The gesture is recognized after the distance between the fingers changes by more than 20 screen units.
- The fingers must move in substantially opposing directions: the angle between their movement vectors must be greater than 90 degrees.
- Spreading and pinching produce opposite zoom directions.
- Zoom distance is normalized for display DPI.

If a two-finger movement does not satisfy either the pan or pinch thresholds, no navigation gesture starts. A gesture is classified once and keeps that mode until it ends; lift both fingers before switching from pan to pinch or vice versa.

### Tap: select

A tap on a selectable model object selects it exclusively. A tap on empty model space clears the selection. If multi-selection mode is already active, a tap toggles the touched object instead.

### Press and hold: add or remove from selection

On a device currently operating in touch-input mode, hold a finger on a selectable object for **0.75 seconds**. The touched object is toggled in the current selection. Moving into a drag or lifting before the threshold cancels the pending hold action.

### Double-tap: contextual model action

A double-tap is recognized from the platform tap count or from two taps occurring within approximately 0.3 seconds. In a focused 3D view it can:

- fit the camera to the selected object when that object permits move-to-on-double-click;
- enter the nearest visible projection sphere when a laser-scan element is selected;
- begin editing a selected comment;
- restart line tracking from the selected object while line tracking is active.

The applicable subsystem decides the result. Double-tap therefore has no effect when the selection or current mode does not support one of these actions.

### Window Zoom: one-finger rectangle

When **Window Zoom** is enabled, a one-finger drag draws a rectangular overlay instead of orbiting. Release the finger to zoom the 3D camera to that rectangle. An empty rectangle is ignored.

## Walk mode

Walk mode treats the centroid of up to three active touches as a floating joystick. The joystick appears after the touch centroid leaves a DPI-scaled dead zone. The number of fingers present when the gesture begins selects the joystick function.

| Fingers | Horizontal drag | Vertical drag |
| --- | --- | --- |
| One | Turn left/right | Move forward/backward |
| Two | Strafe left/right | Elevate down/up |
| Three | Turn left/right | Look vertically |

The joystick center is reset whenever fingers are added or removed. Lift all fingers to hide it. A pinch can also change the camera distance from a focused avatar, constrained by the walk camera's minimum and maximum distance.

## PDF documents

PDF gestures are active only while the PDF view owns the interaction and no PDF markup tool is active.

- **One-finger drag:** pans the page.
- **Two-finger parallel drag:** also pans the page.
- **Pinch or spread:** zooms around the midpoint of the fingers.
- **Window Zoom enabled:** one-finger drag draws a zoom rectangle; pinch zoom is disabled while this mode is enabled.

The PDF controller can optionally restrict zoom to positions inside the page. Releasing the final touch ends the PDF interaction and commits any post-gesture update.

## IntelliPID and other 2D drawing views

- **Two-finger parallel drag:** pans an IntelliPID view while the pointer is over it.
- **One-finger drag:** pans when the view's Pan tool is active.
- **Pinch or spread:** changes the orthographic zoom around the gesture midpoint, subject to the view's zoom limits.
- **Pinch or spread on a directional drawing image:** scales the image around the gesture midpoint when the image uses the `DirectionalZoom` component.

Touching with more than one finger suppresses ordinary IntelliPID element highlighting so navigation is not mistaken for element selection.

## UI gestures

### Tap

A tap is processed as the primary pointer action. It activates buttons, tabs, tree nodes, menu items, links, toggles, and similar controls. Tapping a text field focuses it and, on mobile or browser-streamed touch clients, can open the on-screen keyboard.

### Drag to scroll

Touch-dragging scrolls supported lists and toolbars. The main left-menu toolbars install explicit vertical drag-scrolling; standard Unity scroll views may also provide their normal touch scrolling.

For browser-streamed touch input, scrolling begins when the finger moves more than 5 panel units along the scroll view's permitted axis within 1 second. This threshold leaves room for a tap or long-press to be recognized.

### Streamed long-press: context menu

In the browser-streamed client, hold one finger on the same UI element for more than **1 second**, moving no farther than 5 panel units. On release, the element's context menu opens if that element exposes one. Scrolling, leaving the original element, or using more than one finger cancels the action.

This streamed UI long-press is separate from the native 0.75-second model-selection hold.

### Direct manipulation

Draggable controls continue to use pointer drag behavior on touch. Examples include movable tabs, splitters, sliders, and application-specific drag-and-drop targets. The exact result belongs to the touched control rather than to a global multi-touch gesture.

---

# UDiTH App — Camera

# Camera

**UDiTH** offers three camera modes. After loading a new model, **UDiTH** starts in Orbit mode by default.

You can select another mode by clicking the <img src="./media/ButtonCameraMode.png" width="16" height="16"> **Select camera mode** icon in the top-left corner of the screen.

<table>
<tr><th>Mode</th><th></th><th>Hold left mouse button</th><th>Hold right mouse button</th><th>Mouse wheel</th><th>Keyboard</th></tr>
<tr><td>

<img src="./media/ButtonCameraMode.png" width="44" height="44">

</td><td>

Orbit mode

</td><td>

Rotate the camera around the centred object

</td><td>

Move the camera forwards or backwards

</td><td>

Move the camera towards or away from the mouse pointer

</td><td>

<strong>W, A, S, D</strong> or the arrow keys: move camera
<strong>Shift + W</strong>, <strong>Shift + S</strong>: raise or lower camera
<strong>Space</strong>: raise camera

</td></tr>
<tr><td>

<img src="./media/ButtonFlightMode.png" width="44" height="44">

</td><td>

Flight mode

</td><td>

Rotate the camera

</td><td>

Move the camera forwards or backwards

</td><td>

Move the camera towards or away from the mouse pointer

</td><td>

<strong>W, A, S, D</strong> or the arrow keys: move camera
<strong>Shift + W</strong>, <strong>Shift + S</strong>: raise or lower camera
<strong>Space</strong>: raise camera

</td></tr>
<tr><td>

<img src="./media/ButtonWalkMode.png" width="44" height="44">

</td><td>

Walk mode

</td><td>

Rotate the camera left or right, and move forwards or backwards

</td><td>

Two fingers: move the camera left or right, and up or down

</td><td>

Three fingers or press and hold the mouse wheel: rotate the camera

</td><td>

</td></tr>
</table>

<table>
<tr>
<td>

<img src="./media/ButtonBubbleView.png" width="44" height="44">

</td><td>

<strong>Toggle display of 360° panorama centres</strong>

A light background indicates that this option is active. Panorama locations then appear as red spheres in the model. To deactivate it, click the button again.

</td></tr>
<tr>
<td>

<img src="./media/ButtonOrthographicCamera.png" width="44" height="44">

</td><td>

<strong>Toggle orthographic camera mode</strong>

To deactivate this function, move the camera or click the button again.

</td>
</tr>
<tr><td>

<img src="./media/ButtonReset.png" width="44" height="44">

</td><td>

<strong>Reset view</strong>

Clicking this icon resets all options, such as camera type, position, highlighting, and more. Everything is restored to its default state.

</td></tr>
<tr><td>

<img src="./media/ButtonClippingPlane.png" width="44" height="44">

</td><td>

<strong>Set near/far clipping plane</strong>

Sets the distance from the camera at which objects are rendered. For example, if the far clipping plane is set to 100 metres, objects beyond that distance are not rendered.

</td></tr>
<tr><td>

<img src="./media/ButtonInspect.png" width="44" height="44">

</td><td>

<strong>Set camera position</strong> 
Places the camera at the specified position, facing the given target.

</td></tr>
<tr><td>

<img src="./media/ButtonGoToGPS.png" width="44" height="44">

</td><td>

<strong>Go to GPS position</strong> 
Places the camera at the specified GPS location. This function requires an active GPS device.

</td></tr>
<tr><td>

<img src="./media/ButtonFollowGPS.png" width="44" height="44">

</td><td>

<strong>Follow GPS position</strong>

This function follows the live GPS signal and requires an active GPS device.

</td></tr>
</table>

> Any comments and views will be lost unless they are saved in a configuration file.

<table>
<tr><th>Action</th><th>Alternative action</th><th><img src="./media/ButtonCameraMode.png" width="44" height="44"></th><th><img src="./media/ButtonFlightMode.png" width="44" height="44"></th><th><img src="./media/ButtonBubbleView.png" width="44" height="44"></th><th><img src="./media/ButtonWalkMode.png" width="44" height="44"></th><th>2D</th></tr>
<tr><td>

<img src="./media/MouseLeftClick.png" width="44">

</td><td>

Touch one-finger click

</td><td colspan="5">Select object

</td></tr>
<tr><td>

<img src="./media/MouseLeftDoubleClick.png" width="44">

</td><td>

Touch one-finger double-click

</td><td colspan="4">Jump to the closest panorama or markup

</td><td>

</td></tr>
<tr><td>

<img src="./media/MouseScrollWheelUp.png" width="44">

</td><td>

</td><td rowspan="2" colspan="5">Move forward/zoom in

</td></tr>
<tr><td>

<kbd>↑</kbd>

</td><td>

<kbd>W</kbd>

</td></tr>
<tr><td>

<img src="./media/MouseScrollWheelDown.png" width="44">

</td><td>

</td><td rowspan="2" colspan="5">Move backwards / zoom out</tr>
<tr><td>

<kbd>↓</kbd>

</td><td>

<kbd>S</kbd>

</td></tr>
<tr><td>

<kbd>Shift</kbd>-<kbd>↑</kbd>

</td><td>

<kbd>Shift</kbd>-<kbd>W</kbd>

</td><td colspan="4">Move up

</td><td>

</td></tr>
<tr><td>

<kbd>Shift</kbd>-<kbd>↓</kbd>

</td><td>

<kbd>Shift</kbd>-<kbd>S</kbd>

</td><td colspan="4">Move down

</td><td>

</td></tr>
<tr><td>

<kbd>←</kbd>

</td><td>

<kbd>A</kbd>

</td><td colspan="2">Move left

</td><td colspan="2">Rotate left

</td><td>

</td></tr>
<tr><td>

<kbd>→</kbd>

</td><td>

<kbd>D</kbd>

</td><td colspan="2">Move right

</td><td colspan="2">Rotate right

</td><td>

</td></tr>
<tr><td>

<img src="./media/MouseLeftMove.png" width="44">-↑

</td><td>

Swipe

</td><td>

Orbit up

</td><td colspan="2">Rotate up

</td><td>

Move forward

</td><td>

</td></tr>
<tr><td>

<img src="./media/MouseLeftMove.png" width="44">-↓

</td><td>

Swipe

</td><td>

Orbit down

</td><td colspan="2">Rotate down

</td><td>

Move backward

</td><td>

</td></tr>
<tr><td>

<img src="./media/MouseLeftMove.png" width="44">-←

</td><td>

Swipe

</td><td>

Orbit left

</td><td colspan="3">Rotate left

</td><td>

</td></tr>
<tr><td>

<img src="./media/MouseLeftMove.png" width="44">-→

</td><td>

Swipe

</td><td>

Orbit right

</td><td colspan="3">Rotate right

</td><td>

</td></tr>
<tr><td>

MMB-Press&Drag-↑

</td><td>

</td><td colspan="3">Pan/scroll up

</td><td>

Rotate up

</td><td>

</td></tr>
<tr><td>

MMB-Press&Drag-↓

</td><td>

</td><td colspan="3">Pan/scroll down

</td><td>

Rotate down

</td><td>

</td></tr>
<tr><td>

MMB-Press&Drag-←

</td><td>

</td><td colspan="3">Pan/scroll left

</td><td>

Rotate left

</td><td>

</td></tr>
<tr><td>

MMB-Press&Drag-→

</td><td>

</td><td colspan="3">Pan/scroll right

</td><td>

Rotate right

</td><td>

</td></tr>
</table>

### Pan Mode
<table>
<tr><td>

<img src="./media/ButtonPan.png" width="44" height="44">

</td><td>

<strong>Toggle Pan Mode</strong> activates the panning feature. Alternatively, you can hold down the middle mouse button, press and hold the mouse wheel, or press the left and right mouse buttons simultaneously. On mobile devices, simply swipe the screen with two fingers to pan.

</td></tr>
</table>

## 360° Panoramas

<table>
<tr><td>

<img src="./media/image222.png" width="270">

</td><td>

When you click one of the spheres, the camera moves to its centre and overlays the model with the panorama image captured at that position. While inside a panorama sphere, you can only rotate the camera. You can zoom in and out using the mouse wheel, or by pinching and spreading on mobile devices.

</td></tr>
</table>

The additional context menu contains the panorama options:

<table>
<tr><td Rowspan="3"><img src="./media/360PanoramaMenu.png" width="300">

</td>
<td>

You can select the most recently activated panoramas in chronological order from the drop-down list. Selecting one moves the view to that specific sphere.

</td></tr>
<tr><td>

The <strong>Opacity</strong> slider adjusts the transparency of the overlaid panorama image, allowing you to increase or decrease its visibility.

</td></tr>
<tr><td>

To exit panorama mode, click the <strong>Toggle display of 360° panorama centres</strong> button [<img src="./media/ButtonBubbleView.png" width="20" height="20">] again.

</td></tr>

</table>

> When you select a laser scan object, any visible panorama spheres from the clicked point are displayed in the attributes field within the additional context menu. Clicking one of them allows you to jump directly to that specific sphere.

## Minimap

<table>
<tr><td>

<img src="./media/SettingsToggleMiniMap.png" width="300">

</td>
<td>

Click the <strong>Settings</strong> button [<img src="./media/ButtonSettings.png" width="20" height="20">] on the left side of the screen to access the quick navigation controls in the lower-right corner of the screen, regardless of the camera mode. Scroll down to <strong>3D user interface</strong> and toggle <strong>Minimap</strong>.

</td></tr> 
</table>

<table>
<tr><td rowspan="3"><img src="./media/MiniMap.png" width="300">

</td>
<td>

You can then <strong>move</strong> the camera using the <em>left mouse button</em> and <strong>rotate</strong> it using the <em>right mouse button</em>, directing it precisely to a point on the map with a left or right click. The 90° angle shown indicates the camera's field of view.

</td></tr>
<tr><td>

You can <strong>zoom</strong> in and out using the <strong>magnifying glass</strong> icons.

</td></tr>
<tr><td>

You can use the <strong>bar</strong> on the right to change the camera's <strong>height</strong>. The lower half represents the total height of the model.

</td></tr>
</table>

## Common Views Cube

<table>
<tr><td>

<img src="./media/SettingsToggleCommonViewsCube.png" width="300">

</td>
<td>

Click the <strong>Settings</strong> button [<img src="./media/ButtonSettings.png" width="20" height="20">] on the left side of the screen to access the quick navigation controls in the lower-right corner of the screen, regardless of the camera mode. Scroll down to <strong>3D user interface</strong> and toggle <strong>Common views cube</strong>.

</td></tr> 
</table>

<table>
<tr><td rowspan="3"><img src="./media/IconViewCube.png" width="140" height="149">

</td><td>

The cube in the upper-right corner of the screen indicates direction: <strong>N</strong> - North, <strong>W</strong> - West, <strong>S</strong> - South, <strong>E</strong> - East, <strong>U</strong> - Up, <strong>D</strong> - Down.

</td><tr><td>

Clicking any face of the cube, or the small spheres connecting two or three faces, orients the camera to look directly at that face or corner. For example, clicking <strong>Up</strong> directs the camera to point straight down.

</td></tr>
<tr><td>

In the <strong>Settings</strong> menu, you can toggle the visibility of the cube, switch between compass and XYZ modes, and make other adjustments.

</td></tr></tr>
</table>

To customise the text on the cube faces, modify the model configuration file and add the following line to `defaultconfig.upv`:

```"CommonViewsCubeText": " S \| E \| N \| W \| U \| D "```

This displays the specified text on the faces of the cube.

---

# UDiTH App — Objects

# Objects

## Select Objects

Select one or more objects by clicking the checkbox next to them or their name in the navigation bar (tree view) at the top of the screen.

<img src="./media/SelectObjects.png" width="600" height="">

### Common Attribute Display during Multi-Selection

To view common attributes for multiple objects, either hold down the **Ctrl** key while selecting them or click predefined groups or plant sections. This feature allows the simultaneous display of shared standard and custom attributes for up to 1,000 objects.

## Select Clipping Mode

<table>
<tr><td>

<img src="./media/image62.jpg" width="44" height="44">

</td><td>

**Select clipping mode**

To improve object visibility, clip the view to exclude the area around the selected objects.

</td></tr>
</table>

> After deactivating clipping mode, it may take a few moments for the entire model to load again.

Please wait until the progress bar in the status bar stops changing.

Select from three clipping modes and customise them using the available options:

### Volume Clipping

<table>
<tr><td>

<img src="./media/ButtonVolumeClipping.png" width="44" height="44">

</td><td>

**Volume clipping**

Select an object and surround it with a rectangular clipping box that matches its size. This ensures that only fully enclosed objects remain visible.

</td></tr>
</table>

<table>
<tr><td>

<img src="./media/image63.jpg" width="44" height="44">

</td><td>

**Customise clipping** 

To change the dimensions of the clipping box, move the slider between 0 and 25 metres or drag one side of the box directly. To reposition the box, drag the coloured arrows.

</td></tr>
</table>

<table>
<tr><td>

<img src="./media/ButtonFit.png" width="44" height="44">

</td><td>

 **Centre and fit selected objects**

This option automatically adjusts the view if the size of the clipped volume changes.

</td></tr>
</table>

<table>
<tr><td>

<img src="./media/image67.jpg" width="44" height="44">

</td><td>

**Save volume to clipboard**

Saves the volume of the current clipping to the clipboard so that you can paste it into an Excel file.

</td></tr>
</table>

> You can enlarge the box beyond 25 m and reduce it to a size smaller than the original object to create a cross-section.

If you drag one side to a value below 0 m, the opposite side of the box also moves.

### IntelliClipping

<table>
<tr><td>

<img src="./media/ButtonIntelliClipping.png" width="44" height="44">

</td><td>

In **IntelliClipping**, the original clipping box follows the shape of the selected object itself, which is surrounded by the default selection box.

</td></tr>
</table>

-   Use the slider to include all objects within the specified distance of the selected object in the clipping area, from 0 to 25 metres.
-   Selecting **None** will display only the selected object.

### Inverse Clipping

<table>
<tr><td>

<img src="./media/ButtonInverseClipping.png" width="44" height="44">

</td><td>

**Inverse clipping** provides a clear view towards the centre of the selected objects by clearing one quadrant along the x, y, and z axes. All other quadrants remain visible as usual.

</td></tr>
<tr><td>

<img src="./media/ButtonGrid.png" width="44" height="44">

</td><td>

**Show/hide grid planes**

You can add a grid that responds to the camera's distance from the object.

The grid is displayed as 1 m x 1 m, 10 m x 10 m, or 100 m x 100 m depending on the camera's distance from the object.

To change the size of the grid, drag the sides of the quadrant.

</td></tr>
</table>

> Similar to inverse clipping, Walk mode with an additional object or person always keeps a clear corridor between the object and the camera.

<table>
<tr><td>

<img src="./media/image67.jpg" width="44" height="44">

</td><td>

**Save volume to clipboard**

Saves the volume of the current clipping to the clipboard so that you can paste it into an Excel file.

</td></tr>
</table>

**Example:**

<table>
<tr><th></th><th>min Y</th><th>min X</th><th>min Z</th><th>max Y</th><th>max X</th><th>max Z</th></tr>
<tr><td>

NewVolume

</td><td>

3023.2Z

</td><td>

2375.12

</td><td>

67.19

</td><td>

3037.21

</td><td>

2393.12

</td><td>

72.15

</td></tr>
</table>

## Centre and Fit

<table>
<tr><td>

<img src="./media/ButtonFit.png" width="44" height="44">

</td><td>

**Centre and fit selected objects**

Select the objects you want to centre in the window and then click this button.

If nothing is selected, all elements are fitted into the window.

</td></tr>
</table>

## Toggle Visibility

<table>
<tr><td>

<img src="./media/image88.png" width="44" height="44">

</td><td>

**Toggle selected objects visibility**

To hide or show objects, select them and click this button.

</td></tr>
</table>

## Show Only

<table>
<tr><td>

<img src="./media/ButtonLimitSearch.png" width="44" height="44">

</td><td>

**Show only selected objects** 

This function hides all objects except the selected ones. Toggle it to activate or deactivate the filter.

</td></tr>
</table>

## Highlighting Objects

<table>
<tr><td>

<img src="./media/ButtonHighlight.png" width="44" height="44">

</td><td>

**Highlight selected objects**

All objects except the selected ones appear greyed out and semi-transparent.

Toggle the button again to deactivate this function.

</td></tr>
</table>

## Object Colours
### Colourise Selected Objects

<table>
<tr><th><img src="./media/ButtonColourOptions.png" width="44" height="44"></th><td>

**Colourise selected objects**

Change the display colour of selected objects by opening the colour menu.
</table>

This menu is divided into four main sections:

<img src="./media/IntelliPID_image135.png" width="400" height="253">

#### Colour Options

<table>
<tr><th Rowspan="2"><img src="./media/ButtonColouring.png" width="44" height="44"></th><td>

**Advanced colour selection** opens a pop-up menu for detailed colour selection, allowing you to choose from the full RGB colour spectrum:

</td></tr>
</table>

<table>
<tr><td>

<img src="./media/IntelliPID_image136.png" width="200" height="">

</td><td>

Once you have chosen the required colour, confirm [<img src="./media/IntelliPID_image137.jpg" width="16" height="16">] or cancel [<img src="./media/IntelliPID_image61.png" width="16" height="16">] your selection.

The selected object or objects are displayed immediately in the chosen colour.

</td></tr>
</table>

<table>
<tr><th><img src="./media/IntelliPID_image17.jpg" width="44" height="44"></th><td>

**Pick colours** lets you take a colour from a 3D object and save it under **Custom colours** so that you can use it again later.

Activate the function, click an object to pick its colour, and then select a white custom colour square to save it.

</td></tr>
<tr><th><img src="./media/IntelliPID_image139.png" width="44" height="44"></th><td>

**Clear style** resets the colour of the selected objects to their default colour, which was initially assigned during the building process.

</td></tr>
<tr><th><img src="./media/IntelliPID_image140.png" width="44" height="44"></th><td>

**Reset all colours** restores **all** assigned colours for **all** objects to the default colours set during the building process.

</td></tr>
</table>

#### Default Colours

The **Default colours** section provides a basic set of predefined colours that can be applied to the currently selected objects.

#### Custom Colours

**Custom colours** are created by the user using the colour picker function described above. Once defined, they can be used in the same way as the default colours.

#### Opacity Setting

The slider at the bottom adjusts opacity. The leftmost position is fully transparent, while the rightmost position makes the selected objects fully opaque.

### Creating a Colour File

<table>
<tr><td>

<img src="./media/image75.jpg" width="44" height="44">

<img src="./media/ButtonLoadColourFile.png" width="44" height="44">

</td>
<td>

Your model is loaded with a default colour file.

You can also create a **Custom colour file** and load it via the file management menu.

</td></tr>
</table>

Custom colour files can be created in Excel by entering a colour and a corresponding condition. You can use colour names or HTML colour codes beginning with `#`, followed by six digits representing the intensity of red, green, and blue, with two optional digits for opacity.

For **Condition**, you can enter attributes such as task, piping, name, system path, or any other relevant attribute.

**Aspect** is an optional value used to colour insulation and/or simple physical piping.

**Example:**

<table>
<tr><th>Colour</th><th>Condition</th></tr>
<tr><td>

red

</td><td>

Task=Equipment

</td></tr>
<tr><td>

blue

</td><td>

Task=Structure

</td></tr>
<tr><td>

\#FF00FF22

</td><td>

Task=Supports

</td></tr>
<tr><td>

red

</td><td>

Task=Cableway

</td></tr>
<tr><td>

red

</td><td>

Task=Conduit

</td></tr>
<tr><td>

red

</td><td>

Task=HVAC

</td></tr>
<tr><td>

\#FF00FF

</td><td>

piping

</td></tr>
</table>

## Measure Distances

<table>
<tr><td>

<img src="./media/ButtonMeasurements.png" width="44" height="44">

</td><td>

**Measure distances** creates markers that can be placed on the outlines and corners of 3D objects. The markers can be repositioned by dragging them.

</td></tr>
<tr><td>

<img src="./media/ButtonMeasureDistance.png" width="44" height="44">

</td><td>

**Create new distance measurement** creates two connected markers and displays the distance between them, including the distances in the XYZ planes.

</td></tr>
<tr><td>

<img src="./media/ButtonMeasureMinimumDistance.png" width="44" height="44">

</td><td>

**Create new minimum distance measurement** creates two connected markers and displays the minimum distance between two selected objects.

</td></tr>
<tr><td>

<img src="./media/ButtonNewMeasurement.png" width="44" height="44">

</td><td>

**Create new coordinate measurement** creates a single measurement marker together with its coordinate information.

</td></tr>
<tr><td>

<img src="./media/EastAxis.png" width="44" height="44"><img src="./media/NorthAxis.png" width="44" height="44"><img src="./media/UpAxis.png" width="44" height="44">

</td><td>

**Prevent East-Axis/North-Axis/Up-Axis movement**

</td></tr>
<tr><td>

<img src="./media/IgnoreIntelligentTargetting.png" width="44" height="44">

</td><td>

**Ignore intelligent targetting** is explained in more detail in the examples below.

</td></tr>
<tr><td>

<img src="./media/ButtonCircle.png" width="44" height="44">

</td><td>

This button **Estimates the diameter** of cylindrical objects, such as pipes.

This feature is especially useful for laser scans and also displays a red diameter as a visual aid.

</td></tr>
</table>

><img src="./media/image289.png" width="330" height="165">

### Intelligent Targeting

<table>
<tr><th>**Activated** intelligent targeting</th><th>**Deactivated** intelligent targeting</th></tr>
<tr><td>

<img src="./media/image290.png" width="375" height="259">

</td><td>

<img src="./media/image291.png" width="375" height="259">

</td></tr>
</table>

* With **intelligent targeting** activated, the marker selects positions only on the previously selected object. In the example above, the right-hand marker, highlighted in blue, can be positioned at a point behind the red vertical beam.
* When **intelligent targeting** is deactivated, the right-hand marker, highlighted in blue, selects a position on any object over which it hovers. In this case, the operator's viewing angle becomes an important factor.

> For optimal measuring in models built with **AdapterForSmartReview**, use the following settings:

`Stroking Tolerance`: `100`

Enable `Advanced Graphics Handling for measuring and line tracking`.

> When you activate `Object Recognition` in models built with **AdapterForDocuments**, additional measuring points, such as pipe centres, become available.

---

# UDiTH App — Tools

# Tools

<table>
<tr><td>

<img src="./media/ButtonTools.png" width="44" height="44">

</td><td>

The **Tools** menu contains specialised functions and options for working with your model.

</td></tr>
</table>

## Centre of Gravity

<table>
<tr><td>

<img src="./media/ButtonCenterOfGravity.png" width="44" height="44">

</td><td>

**Calculate centre of gravity**

</td> 
<td>

<img src="./media/ButtonCOGDownload.png" width="44" height="44">

</td><td>

**Export centre of gravity data** as an Excel file.

</td></tr>
</table>

There are two methods of calculating the centre of gravity (COG) of objects in your model.

- If the necessary attribute data is available in Smart3D or through special labels in **AdapterForSmartReview** (`Dry CG`), it is used to calculate the COG.

- Alternatively, UDiTH will calculate an approximate COG.

> Objects are highlighted in green if they have COG data and in red if they lack it.

## Highlight Linked Objects

<table>
<tr><td>

<img src="./media/ButtonLinkedObjects.png" width="44" height="44">

</td><td>

**Highlight linked objects**

Clicking this icon greys out all objects that do not have a link. Objects in UDiTH can be linked to documents, maintenance manuals, equipment lists, websites, and more.

</td></tr>
</table>

## Automatic Labelling

<table>
<tr><td>

<img src="./media/ButtonAutoLabeling.png" width="44" height="44">

</td><td>

**Toggle automatic labelling**

</td></tr>
</table>

1. Click the **Settings** button [<img src="./media/ButtonSettings.png" width="20" height="20">] on the left side of the screen.

2. In the menu that appears in the bottom-right corner of the screen, scroll down to **Attribute options** and click **Select attributes for automatic labelling**.

<img src="./media/SettingsAutomaticLabelingAttributes.png" width="300">

3. Next, select the required attributes by dragging them into the box on the right:

<img src="./media/AutomaticLabelingAttributes.png" width="400" height="">

><img src="./media/Icon_Info.png" width="16" height="16"> You can type a character on the keyboard to jump directly to that position in the list.

## Activate Line Tracking

<table>
<tr><td>

<img src="./media/ButtonLineTracking.png" width="44" height="44">

</td><td>

**Activate line tracking**

When you select a pipe, the line tracking feature displays arrows at its ends. Clicking these arrows moves directly to the next object in the pipeline.

</td></tr>
</table>

<img src="./media/image156.png" width="714" height="153">

> For the best results in **AdapterForSmartReview**, use the following settings under `Tools > Settings > Advanced`: set `Stroking Tolerance` to `100` and enable `Advanced Graphics Handling for measuring and line tracking`.

### QR Decoder

<table>
<tr><td>

<img src="./media/ButtonQR.png" width="44" height="44">

</td><td>

**Open QR decoder**

You can place QR codes containing a deep link in your plant so that they can be scanned with a mobile device and interpreted by UDiTH.

</td></tr>
</table>

1. Click **Open QR decoder**. A new tab opens and activates the camera on your mobile device.

2. If a QR code containing a deep link is detected, UDiTH follows it automatically.

3. Otherwise, refer to **Linking** in this manual for more information on how to create a deep link.

You can then use a QR code generator to create a QR code for your link.

---

# UDiTH App — Additional objects

# Additional Objects

<table>
<tr><td>

<img src="./media/ButtonAvatar.png" width="44" height="44">

</td><td>

**Open additonal objects panel**

Additional objects help you visualise the size of your model and take a virtual tour.

Choose between vehicles and people, then drag the avatars to the required location.

</td></tr>
</table>

### Placing, Moving and Deleting Additional Objects

<img src="./media/AdditionalObjects.png" width="350" height="">
<img src="./media/image102.png" width="44" height="44">
<img src="./media/image103.png" width="44" height="44">

After placing an avatar, you can move it and rotate it horizontally by dragging the corresponding icons.

You can select an avatar at any time, whether the avatar menu is open or not.

### General Controls

<table>
<tr><td>

<img src="./media/ButtonWalkMode.png" width="44" height="44">

</td><td>

If you select an avatar and enter **Walk mode**, you can walk or drive around using the selected avatar.

</td></tr>
<tr><td>

<img src="./media/ButtonDelete.png" width="44" height="44">

</td><td>

**Delete current object**

All avatars that are not saved in a configuration file are lost when the program is closed.

</td></tr>
<tr><td>

<img src="./media/image104.png" width="44" height="44">

</td><td>

**Drop to ground** moves the bottom of the avatar to the nearest surface above or below with a single click.

</td></tr>
<tr><td>

<img src="./media/ButtonCollision.png" width="44" height="44">

</td><td>

You can **Toggle collision in walk mode**.

</td></tr>
</table>

### Walk Mode

<table>
<tr><td>

<img src="./media/image107.png" width="214" height="199">

</td><td>

* To navigate, hold the left mouse button and drag the mouse, or use your finger on a touch device.
* To switch to another avatar already placed in the model, click it and continue moving.
* You can also use the **W, A, S, D** keys on devices with a keyboard.
* To move up, press **Space**. To descend, press **Shift + S**.

</td></tr>
</table>

### Camera

Move the camera behind, or slightly in front of, the avatar. Similar to inverse clipping mode, this ensures an unobstructed view of the additional object.

### Steps

Your avatar moves up or down to surfaces that are not too high or too low.
This allows you to walk up and down stairs.

---

# UDiTH App — Custom attributes

# Custom Attributes

<table>
<tr><td>

<img src="./media/ButtonCustomAttributes.png" width="44" height="44">

</td><td>

**Open custom attributes panel**

Custom attributes provide a way to add new attributes to a model without having to rebuild the model with the UDiTH Builder.

</td></tr>
</table>

If a valid **attributes.xlsx** file is present in the UDiTH data folder, for example `C:\UDiTH\Model\Demoplant\Data\attributes.xlsx`, UDiTH displays all available attributes, including any custom attributes, in the **Custom attributes** menu on the right-hand side of the screen.

<img src="./media/CustomAttributesConstructionState.png" width="550" height="">

> When you use the **Ctrl** key to select multiple objects, their common attributes are displayed directly.

## Custom Attributes Panel

> For details on creating and configuring custom attributes, please refer to the **Custom Attributes** section in **[Custom settings](https://help.caxperts.com/UDiTH%20App/Appendix%20-%20Custom%20settings%20files)**.

In the **Attributes** tab, **Custom attributes** are shown in a distinct colour. In the standard view, they appear in **yellow** and, unlike regular attributes, can be edited. The editing process depends on the type of custom attribute.

-   When you select a custom attribute in the **Custom Attributes** panel, the objects are coloured accordingly and the same custom attribute in the **Attributes** tab changes from **yellow** to **red**.

-   This can also be achieved by double-clicking a custom attribute. When colouring is applied through the **Custom Attributes** panel, it affects **all** objects. When you double-click a custom attribute instead, objects are coloured according to the **CodeList** colour scheme only when they are clicked in the viewport.

To edit **FreeText** custom attributes, simply type the required value, in the same way as for the **Comment** attribute described earlier. For **CodeList** attributes, a drop-down menu is available from which you can select the required value.

<img src="./media/IntelliPID_image144.png" width="502" height="62">

In addition, CodeList-based custom attributes show not only the selected value, but also the colour assigned to that value in the CodeList colour scheme.

<img src="./media/CustomAttributes3.png" width="400" height="">

### Custom Attribute Tool Bar

<table>
<tr><th><img src="./media/IntelliPID_image147.jpg" width="44" height="44"></th><td>

**Export all changes** 

This option saves all data entered into custom attributes in a single `.xlsx` file together with the relevant information for the items to which the values are linked.

</td></tr>
<tr><th><img src="./media/IntelliPID_image146.jpg" width="44" height="44"></th><td>

**Import all changes** 

This loads previously saved custom attribute values from an `.xlsx` file back into the custom attributes.

</td></tr>
<tr><th><img src="./media/IntelliPID_image148.png" width="44" height="44"></th><td>

**Send email**

Custom attribute changes are logged during the session. Clicking this button generates a summary of the changes, copies it to the clipboard, and opens an email in your default email client. The default recipient and subject are configured in the `Meta` tab of `IntelliPIDattributes.xlsx`. For details, see **[Custom settings](https://help.caxperts.com/UDiTH%20App/Appendix%20-%20Custom%20settings%20files)**.

</td></tr>

<tr><th><img src="./media/ButtonShow.png" width="44" height="44"></th><td>

**Show only active**

Unused custom attributes do not appear in the list of values or in the legend for the currently displayed **P&ID**.

</td></tr>
<tr><th><img src="./media/IntelliPID_image35.jpg" width="44" height="44"></th><td>

**Place legend**

This function displays a legend on the **P&ID**, showing the corresponding colour keys for custom attributes. The legend can be repositioned by dragging it to any location on the **P&ID**. For details about custom attribute CodeLists and the legend, refer to the description below.

</td></tr>
</table>

<img src="./media/IntelliPID_image38.png" width="700" height="316">

### Custom Attribute Drop-Down

The drop-down menu allows you to select from custom attributes with CodeList definitions. `None` is the default selection and uses the standard colouring for the **P&ID**. If a custom attribute with a CodeList is selected, objects on the **P&ID** are coloured according to their custom attribute value, and the legend appears below.

## Custom Attributes File

To use **Custom Attributes**, you must create an Excel file containing the required information. This file, named **attributes.xlsx**, must be placed in the model's data folder. The file contains several tabs, as described in the following sections.

### Definition Tab

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

Id

</td><td>

Identification for custom attributes.

</td></tr>
<tr><td>

Name

</td><td>

The name of the custom attribute as displayed in the UDiTH attributes.

</td></tr>
<tr><td>

SourceAttribute

</td><td>

If the custom attribute is part of an existing attribute, declare the name of the existing attribute here.

</td></tr>
<tr><td>

Filter

</td><td>

See the section <strong>Filter</strong>.

</td></tr>
<tr><td>

DataType

</td><td>

See the section <strong>DataType</strong>.

</td></tr>
<tr><td>

DefaultValue

</td><td>

The default value used when the user hasn't made a selection yet.

</td></tr>
<tr><td>

Parameter1-3

</td><td>

These parameters vary depending on the DataType. See the following sections for details.

</td></tr>
</table>

> You can create multiple **Custom Attributes** with identical names. To do this, use the same **Id** and **Name**, but assign different **SourceAttributes**.

### DataType

<table>
<tr><th>Name</th><th>Description</th><th>Parameter1</th><th>Parameter2</th><th>Parameter3</th></tr>
<tr><td>

Calculation

</td><td>

Simple predefined calculations. See the section <strong>Calculation</strong>.

</td><td>

Calculation identifier

</td><td>

-

</td><td>

-

</td></tr>
<tr><td>

CodeList

</td><td>

A drop-down list with differently coloured options. See the section <strong>CodeList</strong>.

</td><td>

CodeList identifier

</td><td>

Linked CodeList attribute

</td><td>

-

</td></tr>
<tr><td>

FreeText

</td><td>

Any text

</td><td>

Character limit for the text box; use 0 for no limit

</td><td>

-

</td><td>

-

</td></tr>
<tr><td>

Numeric

</td><td>

Any number

</td><td>

Minimum value

</td><td>

Maximum value

</td><td>

Number of decimal places

</td></tr>
</table>

### Filter

The **Filter** tab in the configuration file contains conditions used to group items by specific parameters. Instead of the filter name, the filter condition itself can be written directly in the **Definition** tab, as long as it is entered in a single cell.

<img src="./media/image145.png" width="518" height="171">

**Example**

-   `filter1` could not be written in the **Definition** tab, while `filter2` would work.

-   The `filter1` attribute is applied to all objects that meet the following criteria: the pipeline attribute starts with `03-LC-1`, is exactly `02-LC-1001`, or is exactly `Pipeline=PR01-LS-2001`.

### CodeList

<table>
<tr><td>

<img src="./media/image146.png" width="203" height="279">

</td><td>

The **CodeList** tab contains the details for the DataType `CodeList` used in the **Definition** tab. CodeLists generate drop-down menus in UDiTH for the custom attribute.

</td></tr>
<tr><td>

<img src="./media/image147.png" width="267" height="439">

</td><td>

The **Name** is shown in UDiTH. Each following row without a name belongs to the attribute name specified most recently. For example, after `CS_State`, all values and colours from `B2` to `C6` are part of the `CS_State` attribute.

</td></tr>
</table>

**Value:** The text for the contents of the custom attribute drop-down in UDiTH.

**Color:** The colour of the square next to the values in the attribute drop-down. When filtering by attributes, this colour is also used for the corresponding 3D object in the main view.

### Calculation

Calculations are predefined functions that are executed for each element.

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

TOS

</td><td>

`TOS` (Top Of Steel) returns the elevation in metres at the highest point of the element.

</td></tr>
<tr><td>

BOS

</td><td>

`BOS` (Bottom Of Steel) returns the elevation in metres at the lowest point of the element.

</td></tr>
<tr><td>

ElementColor

</td><td>

Returns the element's current primary colour as a hex code.

</td></tr>
<tr><td>

OriginalColor

</td><td>

Returns the element's original primary colour as a hex code.

</td></tr>
<tr><td>

VisibleColor

</td><td>

Returns the element's visible colour as a hex code, taking temporary UDiTH features into account, for example CodeList colour, highlighting, or centre of gravity.

</td></tr>
<tr><td>

StaticText

</td><td>

Displays the value from `DefaultValue`.

</td></tr>
<tr><td>

Bounds

</td><td>

Returns the bounding coordinates. It uses a format string as `Parameter1` and inserts the values accordingly. The default format is `(%lowX%, %lowY%, %lowZ%) (%centerX%, %centerY%, %centerZ%) (%highX%, %highY%, %highZ%)`.

</td></tr>
</table>

### Meta

The **Meta** worksheet tab is used for general information.

<table>
<tr><th>Key</th><th>Value</th></tr>
<tr><td>

Email_To

</td><td>

Preset value used as the recipient when sending a change-set email

</td></tr>
<tr><td>

Email_Subject

</td><td>

Preset value used as the subject when sending a change-set email

</td></tr>
<tr><td>

Workcard_Header

</td><td>

Overrides the work card text header

</td></tr>
<tr><td>

WorkcardMail_To

</td><td>

Like `Email_To`, but for work cards

</td></tr>
<tr><td>

WorkcardMail_Subject

</td><td>

Work card email subject; can use special keywords

</td></tr>
<tr><td>

WorkcardMail_Body

</td><td>

Prefix for the work card email body; can use special keywords

</td></tr>
</table>

The meta keys **WorkcardMail_Subject** and **WorkcardMail_Body** can use special placeholder values that are filled in automatically when needed.

<table>
<tr><th>Placeholder</th><th>Description</th></tr>
<tr><td>

%workcard%

</td><td>

The `Work` value of the currently selected work card

</td></tr>
<tr><td>

%devicename%

</td><td>

The user's device identifier

</td></tr>
<tr><td>

%date%

</td><td>

Current date

</td></tr>
</table>

## Import / Export All Changes

<table>
<tr><td>

<img src="./media/ButtonExportCustomSettings.png" width="44" height="44"><img src="./media/ButtonImportCustomSettings.png" width="44" height="44">

</td><td>

You can export changes made to custom attributes to an Excel file or import changes made.

</td></tr>
</table>

The Excel file will contain the following information:

<img src="./media/image149.png" width="482" height="161">

## Email

Changes made to **Custom attributes** during the current session are copied to the clipboard and your email software is opened.

---

# UDiTH App — Project tree

# Project Tree

> The tree bar or tree window remains visible even when you open a PDF, P&ID, or isometric.

Click the **Settings** button. You can then switch between the tree view and the tree bar:

<table>
<tr><th><img src="./media/ButtonProjectView.png" width="44" height="44"></th><td>

**Toggle tree view/tree bar** places the project tree in the menu on the right.

</td></tr>
<tr><th><img src="./media/ButtonLineConfig.png" width="44" height="44"></th><td>

**Tree bar** places the project tree at the top of the main window. You can choose attributes to generate a different project tree automatically.

</td></tr>
<tr><th><img src="./media/ButtonTreeConfig.png" width="44" height="44"></th><td>

**Configure project tree**: each attribute defines one level of the project tree.

</td></tr>
</table>

**Example:**

Using **Material** and **Date created** displays all objects grouped by material and then by creation date.

<img src="./media/ProjectView.png" width="300" height="">

---

# UDiTH App — Search tab

# Search Tab

## Search in 3D, Drawings, Documents, and IntelliPID

UDiTH's search function is designed to work across 3D models, 2D drawings, documents, and IntelliPID:

Results are organised by the attributes in which the search string was found. The numbers shown after the attributes indicate the number of matching groups in grey and the affected P&IDs in light blue. When you expand an attribute, the matching groups are displayed as a list, with the corresponding P&ID name highlighted in light blue.

The search function returns two types of result:

<table>
<tr><td Rowspan="2"><img src="./media/IntelliPidSearch.png" width="300">

</td><td>

- **Hard results**: These exactly match the entered search criteria and are displayed in white.
  
- **Soft results**: These include variations of the search string, such as characters that are commonly added or omitted when converting data to another format. For example, both `slab 1 0002` and `slab-1-0002` would return results. These are displayed in light grey.

</td></tr>
</table>

> Ensure that there is enough space to display your search results by dragging the bar below the search box downwards.

Otherwise, only **Select from search results** may be visible.

### Wildcards

You can add wildcards to the beginning or end of your search term:

-   `?` stands for any single character or number.

-   `*` stands for any number of characters or numbers (`a*` matches `a`, `abc`, `a-12`, and so on).

-   With `>`, `<`, `=`, `=>`, and `=<`, you can search for attributes that start with a number. Any numbers following text strings are ignored.

### Search Tools

Use the search tools below the search results to make it easier to identify and select objects.

<table>
<tr><td>

<img src="./media/image60.jpg" width="44" height="44">

</td><td>

Enables or disables automatic camera movement when changing the selected objects.

</td></tr>
<tr><td>

<img src="./media/ButtonHighlight.png" width="44" height="44">

</td><td>

**Highlight all objects of the selected category**

Objects that do not belong to the selection are greyed out and opaque, while selected objects remain fully coloured.

</td></tr>
<tr><td>

<img src="./media/ButtonShow.png" width="44" height="44">

</td><td>

**Show only objects of the selected category**

Objects that do not belong to the selection are hidden, while selected objects remain visible.

</td></tr>
</table>

<table>
<tr><td>

<img src="./media/SearchGoToGroup.png" width="100" height="">

</td><td>

**Go to previous/next group** selects the previous or next group of objects in the selected category and focuses the 3D view on those objects.

</td></tr>
<tr><td>

<img src="./media/SearchGoToObject.png" width="100" height="">

</td><td>

**Go to previous/next part** selects the previous or next part in the selected category and focuses the 3D view on those objects.

</td></tr>
</table>

---

# UDiTH App — Attributes tab

# Attributes Tab

The **Attributes** tab is displayed by default at start-up. It contains all attribute information for the selected item or items that was provided during the building process, whether from the authoring system or the UDiTH SDK.

- Double-click an attribute value to copy it to the clipboard for further use.
- Drag an attribute into the main window to turn it into a comment.
- Right-click an attribute and then select **Add to search filter** to restrict the search to that attribute in the search filter window.

The information shown in the Attributes tab includes:

- The number of objects you have selected.
- A list of links for the selected object or objects.
- All attributes of the selected object or objects.

> In the **Settings** tab, you can choose whether empty attributes are shown or hidden. For `FileName > Equipment`, **Equipment** is displayed.

When a value, such as the one in the **Name** field, can be edited, the following colours indicate the field type:

- **White** for **read-only** fields
- **Light yellow** for **editable** fields
- **Dark yellow** for **custom** attributes

## Links

The upper part of the **Attributes** tab displays the links attached to the selected object. You can attach links to websites, local and cloud-based documents, images, and external systems, depending on the available API.

> Due to security reasons, links to executable programs and scripts are not possible.

> Documents are opened with the standard application defined in Windows for the corresponding file type.

If a link between the 3D model and the P&ID has been established, a link to the associated objects in 3D is displayed. Clicking this link in UDiTH selects the corresponding objects in the 3D model and centres the view on them.

<img src="./media/AttributesLinks.png" width="350" height="">

A linked 3D object also includes a link with the name of the P&ID in its attributes. This allows you to switch easily between the P&ID and 3D views for linked objects.

---

# UDiTH App — Files tab

# Files Tab

<img src="./media/FilesTab.png" width="250" height="">

<table>
<tr><th>Button</th><th>Description</th></tr>
<tr><td>

<img src="./media/ButtonAdd.png" width="44" height="44">

</td><td>

Adds a new folder.

</td></tr>
<tr><td>

<img src="./media/ButtonDelete.png" width="44" height="44">

</td><td>

Deletes the currently selected tree-object.

</td></tr>
<tr><td>

<img src="./media/image75.jpg" width="44" height="44">

</td><td>

Copy a selected tree-object.

</td></tr>
<tr><td>

<img src="./media/ButtonUndo.png" width="44" height="44">

</td><td>

Undoes the last change to the tree.

</td></tr>
<tr><td>

<img src="./media/ButtonRedo.png" width="44" height="44">

</td><td>

Redoes the last undone change to the tree.

</td></tr>
<tr><td>

<img src="./media/ButtonSave.png" width="44" height="44">

</td><td>

Saves the currently selected tree object as a .upvtree file.

</td></tr>
<tr><td>

<img src="./media/ButtonLoad.png" width="44" height="44">

</td><td>

Opens a .upvtree file.

</td></tr>
</table>

The **Files** tab shows a tree structure of the model's work items. You can create additional UDiTH files by using the drop-down on the left side of the menu below the **Attributes** and **Files** tabs.

You can add the UDiTH files listed in the table below to the tree.

## Tree Items

<table>
<tr><th>Symbol</th><th>Description</th></tr>
<tr><td>

<img src="./media/ButtonAdd.png" width="44" height="44">

</td><td>

**Add** drop-down - Adds a folder object to the model root. Subfolders are not currently supported. Folders can be used to store and group all other file types. For example, if several files belong to one P&ID, you can create a folder containing all relevant files for that P&ID.

</td></tr>
<tr><td>

<img src="./media/image256.png" width="44" height="44">

</td><td>

**2D to 3D projection**

</td></tr>
<tr><td>

<img src="./media/image53.jpg" width="44" height="44">

</td><td>

**Animation**

</td></tr>
<tr><td>

<img src="./media/ButtonAppControl.png" width="44" height="44">

</td><td>

**AppControl**

</td></tr>
<tr><td>

<img src="./media/ButtonEdit.png" width="44" height="44">

</td><td>

**Comment** - The comment object contains one comment per node. Comments can be attached to any document element, whether original or sketch-based. Comment objects are always dependencies of the document object. For more details, see **Comment**.

</td></tr>
<tr><td>

<img src="./media/image56.jpg" width="44" height="44">

</td><td>

**Drawing**

</td></tr>
<tr><td>

<img src="./media/ButtonMarkup.png" width="44" height="44">

</td><td>

**Markup** - The markup file enables you to do markups on your documents, including graphical objects.

</td></tr>
<tr><td>

<img src="./media/ButtonMeasurements.png" width="44" height="44">

</td><td>

**Measurement**

</td></tr>
<tr><td>

<img src="./media/ButtonPackage.png" width="44" height="44">

</td><td>

**Package**

</td></tr>
<tr><td>

<img src="./media/ButtonPhoto.png" width="44" height="44">

</td><td>

**Photo**

</td></tr>
<tr><td>

<img src="./media/IntelliPID_image88.jpg" width="44" height="44">

</td><td>

**P&ID sketch** - The sketch file allows you to add sketches to your P&ID, including your custom P&ID symbols.

</td></tr>
<tr><td>

<img src="./media/ButtonNewMeasurement.png" width="44" height="44">

</td><td>

**Point of interest**

</td></tr>
<tr><td>

<img src="./media/ButtonReport.png" width="44" height="44">

</td><td>

["*Report*"](./Report)

</td></tr>
<tr><td>

<img src="./media/ButtonScreenshot.png" width="44" height="44">

</td><td>

[Screenshot](./Screenshot)

</td></tr>
<tr><td>

<img src="./media/image52.jpg" width="44" height="44">

</td><td>

[Sketch](./Sketch)

</td></tr>
<tr><td>

<img src="./media/ButtonSpraying.png" width="44" height="44">

</td><td>

[Spraying](./Spraying)

</td></tr>
<tr><td>

<img src="./media/ButtonShow.png" width="44" height="44">

</td><td>

[View](./View) of the current camera position

</td></tr>
</table>

## Quick Action Buttons

### Momentary Quick Action Buttons

<table>
<tr><td>

<img src="./media/ButtonEdit.png" width="44" height="44">

</td><td>

Edit - starts an editing process in a modal manner.

</td></tr>
<tr><td>

<img src="./media/ButtonInspect.png" width="44" height="44">

</td><td>

View - for example, moves the camera to a saved position.

</td></tr>
<tr><td>

<img src="./media/ButtonSelect.png" width="44" height="44">

</td><td>

Select

</td></tr>
</table>

### Quick Action Toggles

<table>
<tr><td>

<img src="./media/ButtonShow.png" width="44" height="44">

</td><td>

Show or hide item

</td></tr>
<tr><td>

<img src="./media/ButtonLocked.png" width="44" height="44"><img src="./media/ButtonUnlocked.png" width="44" height="44">

</td><td>

Lock or unlock item for modification

</td></tr>
</table>

# File Management

<table>
<tr><td>

<img src="./media/image75.jpg" width="44" height="44">

</td><td>

The **file management** menu includes most options for saving and loading, except packages and views, which are available in their respective menus.

</td></tr>
<tr><td>

<img src="./media/ButtonSave.png" width="44" height="44"><img src="./media/ButtonLoad.png" width="44" height="44">

</td><td>

**Save and load configurations** in a `.upv` file. A configuration includes the current settings, clipping, colourisation, view, tags, and more.

</td></tr>
<tr><td>

<img src="./media/ButtonLoadColourFile.png" width="44" height="44">

</td><td>

Load a **colour file**. You can choose between local and online colour files.

</td></tr>
<tr><td>

<img src="./media/ButtonLoadLinkedFile.png" width="44" height="44">

</td><td>

Load a **link file**. You can either replace the existing links or append new ones to them.

</td></tr>
<tr><td>

<img src="./media/ButtonCreateLink.png" width="44" height="44"> 

</td><td>

**Create a link** and copy it to the clipboard. **Example:** `upvapi://http://demo.universalplantviewer.com/demoPlant/4/0/?uid=0003a998-0000-0000-681a-a3ff06552004`

</td></tr>
<tr><td>

<img src="./media/ButtonReport.png" width="44" height="44">

</td><td>

**Create** a report.

</td></tr>
<tr><td>

<img src="./media/image67.jpg" width="44" height="44">

</td><td>

**Save objects to disk** in OBJ format.

</td></tr>
<tr><td>

<img src="./media/ButtonGenerateIsoPDF.png" width="44" height="44">

</td><td>

**Generate a double-sided ISO PDF**. This will create both an isometric view and an orthographic drawing for better visualisation.

</td></tr>
<tr><td>

<img src="./media/ButtonBlueHelp.png" width="44" height="44">

</td><td>

Open the **manual**.

</td></tr>
</table>

> Check the file names you have entered before saving. There is no warning if the file name already exists.

### Save Configuration File

<table>
<tr><td>

<img src="./media/ButtonSave.png" width="44" height="44">

</td><td>

Choose which information to save:

</td></tr>
</table>

<img src="./media/SaveConfigFile.png" width="250" height="">

### Load Configuration File

<table>
<tr><td>

<img src="./media/ButtonLoad.png" width="44" height="44">

</td><td>

UDiTH automatically loads a default configuration file. You can save a custom configuration file as `defaultConfig.upv` in the same location as `UDiTH.exe`.

</td></tr>
</table>

You can also define the path to the settings file in the URI:
`file://C:\Path\To\UDiTH_Web.html?settings=c:\MySettings\Config.upv`

> To use the URI function, the configuration file must be accessible from the machine where `UDiTH_Web.html` is located. If `UDiTH_Web.html` is hosted on an IIS server, only configuration files located on the IIS server can be loaded. To apply a local settings file, use **File > Load Settings** instead. Relative paths can be used to define the location of settings files.

### Save and Load Objects

<table>
<tr><td>

<img src="./media/image67.jpg" width="44" height="44">

</td><td>

You can save selected objects as `.obj` files to edit them with a modelling tool or duplicate them in your model. Click **Import avatar from disk** in the **additional objects panel**, then click a location in your model to place the object. After that, you can move and rotate it.

</td></tr>
</table>

<img src="./media/image179.png" width="181" height="276">

<table>
<tr><td colspan=2>These objects are treated as additional objects if you load them in the 3D view. You can delete them in the additional objects panel. If you load them in Sketch mode, they are treated like objects from the catalogue.

</td><td>

</td></tr>
<tr><td>

<img src="./media/image180.png" width="209" height="123">

</td><td>

If you save objects as OBJ files, there is a size limit that matches the maximum input size UDiTH can handle. If you export more than 20 objects, the following warning message appears:

</td></tr>
<tr><td>

<img src="./media/image181.png" width="237" height="129">

</td><td>

Before saving, you can choose to swap the height axis **(Z)** with the breadth axis **(Y)**.

</td></tr>
</table>

---

# UDiTH App — 2D to 3D projection

# 2D to 3D Projection

UDiTH allows you to create custom image projections. 2D images are created with **AdapterForDocuments**, while 360° panoramas are created with **AdapterForLaserScans**.

## Create a Projection

1. Start UDiTH and select your model.

2. Create a projection by selecting **2D to 3D Projection** from the **Add** drop-down in the **Files** tab.

<img src="./media/FilesAdd2Dto3D.png" width="250" height="">
<img src="./media/PictureProjection.png" width="200" height="">

> If you do not enter a name for the projection, the name of the selected image will be used.

3. The selected image will be displayed on the right side.

* You can begin placing projection mapping markers on both the 2D image and in the 3D space of your model.

* It is recommended that you position the camera in the 3D space as close as possible to the location from which the image was taken.

* The algorithm that maps the image into the 3D space calculates several possible orientations of the projection sphere. It then chooses the solution closest to the current camera position.

<img src="./media/image254.png" width="650" height="360">

## Mapping a Projection

1. Create a marker and drag it to the correct position in both the 2D and 3D views.

2. Add up to 4 markers to improve accuracy.

3. Move the camera towards the location from which the picture may have been taken to help ensure that the projection is placed correctly.

4. Recalculate the position.

5. If the image is not placed as required, reposition the markers and recalculate until it is placed correctly.

<table>
<tr><td>

<img src="./media/image256.png" width="44" height="44">

</td><td>

**Adds a selection pin to the 2D mapping view and 3D view**

This feature creates colour-coded mapping markers that can be dragged and positioned accurately in both views. You can place up to 4 markers, with at least 3 usually required for reliable mapping.

</td></tr>
<tr><td>

<img src="./media/image258.png" width="44" height="44">

</td><td>

**Removes a selection pin from the 2D mapping view and 3D view**

</td></tr>
<tr><td>

<img src="./media/image260.png" width="44" height="44">

</td><td>

**Changes the focused node**

Selecting a marker centres both the 3D and 2D views. The marker colour reflects its focus status and is red by default.

</td></tr>

<tr><td>

<img src="./media/ButtonReset.png" width="44" height="44">

</td><td>

**Refreshes the projection using the adjusted or pending measurement nodes**

</td></tr>
</table>

## Picture, 360° Panorama, Orthogonal Drawing

Use the drop-down menu to switch between **Picture**, **360° Panorama**, and **Orthogonal Drawing**. Once a calculation has been carried out for the selected image, you can no longer change the image type.

### Picture

<img src="./media/2Dto3D_Picture.png" width="650" height="">

Pictures are standard 2D images projected into the 3D space. Adjust the **Focal length** setting as required using the control to the right of the mapping buttons. In most cases, the focal length is retrieved automatically from the image metadata.

After the position has been calculated, the camera locks onto the projection sphere and behaves in a similar way to **360° panorama** mode.

<table>
<tr><th><img src="./media/image261.jpg" width="44" height="44"></th><td>

Leave the projection sphere and use the camera as usual.

</td></tr>
<tr><th><img src="./media/image202.png" width="88" height="44"></th><td>

Increases opacity of the projected image.

</td></tr>
</table>

### 360° Panorama

<img src="./media/2Dto3D_Panorama.png" width="650" height="">

After the position has been calculated, the camera is locked to the projection sphere. The following button and slider then appear:

<table>
<tr><td>

<img src="./media/image261.jpg" width="44" height="44">

</td><td>

Leave the projection sphere and use the camera as usual.

</td></tr>
<tr><td>

<img src="./media/image202.png" width="88" height="44">

</td><td>

Increases opacity of the projected image.

</td></tr>
</table>

### Orthogonal Drawing

<img src="./media/Drawing_Orthogonal.png" width="70" height="">

To create a projection of an orthogonal drawing, select **2D** from the breadcrumb menu and then select the required drawing. The drawing opens in a tab. While this tab is active, select **2D to 3D Projection** in the **Files** tab on the right.

<img src="./media/2Dto3D_Orthogonal.png" width="650" height="">

<table>
<tr><th><img src="./media/image267.png" width="66" height="27"></th><td>

Select the plane the drawing should be in.

</td></tr>
</table>

`None` means that the plane is defined by the first three markers.
The other options describe the axes to which the plane belongs, for example the X and Y axes shown in the image above.

---

# UDiTH App — Animation

# Animation

<table>
<tr><td>

<img src="./media/image53.jpg" width="44" height="44">

</td><td>

Animations are created from sketches. The required buttons are located in the **Files** tab. Once you have created a sketch, you can create an animation while the sketch remains open, which links the two together.

</td></tr>
</table>

<img src="./media/FilesAddAnimation.png" width="250" height="">

> Multiple animations can be associated with a single sketch.
  
## Animation Player

<img src="./media/image197.png" width="737" height="56"> 

<table>
<tr><td>

<img src="./media/ButtonEdit.png" width="44" height="44">

</td><td>

**Edit** opens the animation editor.

</td></tr>
<tr><td>

<img src="./media/image198.png" width="44" height="44">

</td><td>

**Go to first** jumps to the first keyframe.

</td></tr>
<tr><td>

<img src="./media/image199.jpg" width="44" height="44">

</td><td>

**Go to previous** jumps to the previous timestamp or the beginning of the animation.

</td></tr>
<tr><td>

<img src="./media/image269.png" width="44" height="44">

</td><td>

**Play/Pause** starts or stops the animation.

</td></tr>
<tr><td>

<img src="./media/image201.jpg" width="44" height="44">

</td><td>

**Go to next** jumps to the next timestamp or the end of the animation.

</td></tr>
<tr><td>

<img src="./media/image270.png" width="44" height="44">

</td><td>

**Go to last** jumps to the last keyframe.

</td></tr>
<tr><td>

<img src="./media/image202.png" width="118" height="44">

</td><td>

**Animation timeline/slider**: drag to move the animation to the required point in time.

</td></tr>
<tr><td>

<img src="./media/image203.png" width="224" height="44">

</td><td>

**Timestamp** drop-down menu: select a timestamp to jump to.

</td></tr>
<tr><td>

<img src="./media/image204.png" width="114" height="44">

</td><td>

**Speed** drop-down menu: select the playback speed.

</td></tr>
<tr><td>

<img src="./media/ButtonZoomIn.png" width="44" height="44">

</td><td>

**Zoom in** zooms in on the timeline.

</td></tr>
<tr><td>

<img src="./media/ButtonZoomOut.png" width="44" height="44">

</td><td>

**Zoom out** zooms out on the timeline.

</td></tr>
<tr><td>

<img src="./media/image207.png" width="44" height="44">

</td><td>

**Zoom to fit** fits so the whole timeline is visible.

</td></tr>
</table>

## Animation Editor

<img src="./media/image208.png" width="729" height="73"> 

<table>
<tr><td>

</td><td>

</td></tr>

<tr><td>

<img src="./media/ButtonInspect.png" width="44" height="44">

</td><td>

**Player** goes back to animation player.

</td></tr>
<tr><td>

<img src="./media/image209.png" width="44" height="44">

</td><td>

**Magic keyframe** creates a keyframe for all currently placed objects that are selected in the animation editor hierarchy, using their current settings.

</td></tr>
<tr><td>

<img src="./media/ButtonFiveSeconds.png" width="44" height="44">

</td><td>

**Magic keyframe delay** sets the automatic delay that is added between magic keyframes. You can enter values such as `2.5 s`, `5 s`, `10 s`, or `1 min`.

</td></tr>
<tr><td>

<img src="./media/image269.png" width="44" height="44">

</td><td>

**Play/Pause** starts or stops the animation.

</td></tr>
<tr><td>

<img src="./media/image271.png" width="44" height="44">

</td><td>

**Expand/Collapse** brings up extended animation tools or closes them.

</td></tr>
<tr><td>

<img src="./media/image212.png" width="44" height="44">

</td><td>

**Add keyframe** adds a keyframe for the selected animation parameter.

</td></tr>
<tr><td>

<img src="./media/image213.png" width="44" height="44">

</td><td>

**Remove keyframe** removes the selected keyframe.

</td></tr>
<tr><td>

<img src="./media/image214.png" width="44" height="44">

</td><td>

**Remove parameter** removes the selected parameter row.

</td></tr>
<tr><td>

<img src="./media/image215.png" width="44" height="44">

</td><td>

**Transition curve view** shows the transition curves, including lines and splines, between values. Drag them to change their shape.

</td></tr>
<tr><td>

<img src="./media/ButtonZoomIn.png" width="44" height="44">

</td><td>

**Zoom in** zooms in on the timeline, centring on the currently selected position marked by the red line.

</td></tr>
<tr><td>

<img src="./media/ButtonZoomOut.png" width="44" height="44">

</td><td>

**Zoom out** zooms out on the timeline.

</td></tr>
<tr><td>

<img src="./media/ButtonAutoLabeling.png" width="44" height="44">

</td><td>

**Add timestamp**: drag and drop this button onto the timeline to create a new timestamp. A dialogue box then appears, allowing you to name the new timestamp. If you drag an existing timestamp, this icon changes into a bin icon. Dragging timestamps into it deletes them.

</td></tr>
<tr><td>

<img src="./media/image207.png" width="44" height="44">

</td><td>

**Zoom to fit** adjusts the view to display the entire timeline.

</td></tr>
</table>

### Timeline View

<table>
<tr><td>

<img src="./media/image217.png" width="177" height="168">

</td><td>

**Project tree** contains all items, parameters etc. involved in this animation.

</td></tr>
<tr><td>

<img src="./media/image218.png" width="78" height="105">

</td><td>

Displays all **Parameters** of the currently selected item in the project tree at the current timeline position. You can edit these parameters by clicking on their values.

</td></tr>
<tr><td>

<img src="./media/image219.png" width="144" height="194">

</td><td>

**Timeline**

The red line is the time slider and shows the current animation position. Drag it to change the current point in time. The blue dots are parameter keyframes for the adjacent item in the project tree; drag them to adjust their positions.

</td></tr>
</table>

## Saving the Animation
**(licence for sketching is needed)**

To save any work on animations, a sketching licence is required. An exception is the animation data from 4D System, which can be loaded and saved without a licence. Animations can be saved as **.upvf** files.

## Creating an Animation Example

1. Create a **Sketch** and place any sketch object, for example a Liebherr crane from the sketch catalogue.

2. Create an **Animation** in the same menu while the sketch is active.

3. Select the sketch object and create an animation keyframe by pressing the **Magic keyframe** button.

<img src="./media/image220.png" width="650" height="454">

4. Move the crane to the required position and press the **Magic keyframe** button again.

<img src="./media/image221.png" width="650" height="454">

5. The animation can now be played in **Player** or **Edit** mode.

---

# UDiTH App — AppControls

# AppControls

AppControls are a feature introduced in UDiTH Version 7.0. They allow regular users, power users, and administrators to extend the functionality of UDiTH. AppControls run inside an embedded browser within UDiTH, allowing them to present their own user interface directly in the application. Communication with UDiTH takes place through an API.

## Using AppControls

AppControls can be stored in the **Files** tab.

1. To create a new **AppControl**, use **Add** to add a new AppControl element in the **Files** tab.

<img src="./media/FilesAddAppControls.png" width="250" height="">

2. Choose the input source. You can provide either a URL or an offline AppControl from your files.

<img src="./media/AppControlURL.png" width="300">

3. Once it has been saved, you can open it by double-clicking it or by clicking the eye icon next to it.

4. The **AppControl** then opens in a separate window.

## Understanding AppControls

AppControls are designed to handle tasks that are not covered by standard user interface components. These customised forms can be used for data entry, information display, preference settings, and more.

They can also be tailored to match your application's specific user interface, requirements, branding, and interaction style.

## When to Use AppControls

AppControls are particularly suitable in the following scenarios:

1. **Specific Functionality:** Your application requires a user interface element that cannot be achieved with standard controls, such as complex data visualization, custom input validation, or specialized data manipulation.

2. **Branding and Aesthetics:** You want to create a consistent and unique look and feel for your application, aligning with your brand identity or user experience goals.

3. **Usability and User Experience:** Custom forms can be designed to enhance the user experience by simplifying complex tasks or streamlining workflows.

## Offline AppControls

UDiTH allows you to embed AppControls directly within the UDiTH tree. This removes the need for a separate web service to access the AppControls. However, certain restrictions still apply.

## Implementing AppControls

For guidance on creating AppControls, please visit the https://github.com/caxperts/AppControls repo, which contains samples and instructions.

## Security

An allowlist is used to ensure that only the AppControls included in that list can be displayed and access the model. This list is stored in a file named `appcontrols.json`, located in the model's `Data` folder. For more information, please visit the [AppControls security documentation](https://github.com/caxperts/AppControls/blob/main/wiki/Security.md).

---

# UDiTH App — WebSocket Feature

# WebSocket Feature

As of UDiTH 2026.2.0, the API supports WebSocket connections between UDiTH App and AppControl Apps. This allows an AppControl App to run in a separate browser window, independent of the UDiTH App.

![WebSocket Overview](media/WebSocket_overview.png)

## Connection Establishment

The general workflow for establishing a connection is as follows:

1. Open the UDiTH App (running locally, not BBV).
2. Open an AppControl web page.
3. On the first attempt, the browser will ask whether the connection should be allowed:

   ![Browser Permission Prompt](media/WebSocket_browser_permission.png)

   The answer is stored in the local browser.

4. The UDiTH App will ask whether the WebSocket connection should be established:

   ![UDiTH Connection Prompt](media/WebSocket_udith_prompt.png)

## Handling Multiple Instances

The AppControl API connects to the first locally running UDiTH App (first in terms of startup time). If additional AppControl Apps are opened, they also connect to the same first UDiTH App. Locally running UDiTH Apps beyond the first one are ignored.

![Multiple Instances](media/WebSocket_multiple_instances.png)
## Troubleshooting

The connection can be refused from two sides: in the UDiTH App and in the browser. These refusals are the typical causes for unsuccessful WebSocket connections.

### Revoking Connection Refusals in UDiTH App

At the bottom of the **Expert Settings**, use the **Clear dis/approved AppControl urls** button to reset all previously approved or denied AppControl URLs.

![Clear AppControl URLs](media/WebSocket_clear_appcontrol_urls.png)

### Revoking Connection Refusals in the Browser

Click on the lock icon next to the URL bar:

![Browser Lock Icon](media/WebSocket_browser_lock_icon.png)

Then set **Apps on device** to **Allow** or **Ask**:

![Browser Apps Permission](media/WebSocket_browser_apps_permission.png)

### Disabeling WebSocket API in an App

Add this code snippet before calling the first API
```typescript
import { Api } from "@caxperts/universal.api/Internal/APIConnector";
Api.disableWebsocketApi = true;
```

Further Information and samples using AppControls and the Universal.API can be found via https://github.com/caxperts/AppControls

---

# UDiTH App — Comment

# Comment

## Quick Comments

<table>
<tr><td>

<img src="./media/ButtonEdit.png" width="44" height="44">

</td><td>

1. Select an object.
2. Go to the **Files** tab and select **Comment** from the **Add** drop-down menu.
3. Enter your text in the box to add a comment to the model.

</td></tr>
</table>

<img src="./media/FilesAddComment.png" width="250">

> You can **drag attributes** from the menu on the right into the main window. These attributes are treated as comments and linked to their object.

### Changing and Deleting Comments

To change a comment, click it so that it is highlighted in red, then enter the new text. Alternatively, you can drag a new attribute into the main window.

<table>
<tr><td>

<img src="./media/ButtonCancel.png" width="44" height="44">

</td><td>

To delete a comment, select it and click the **Remove** icon in the top toolbar.

</td></tr>
<tr><td>

<img src="./media/ButtonSelect.png" width="44" height="44">

</td><td>

To reselect the object linked to the comment, click the **Select** icon. This allows you to move the comment line to a different object. A red marker appears to help you position it accurately.

</td></tr>
<tr><td>

<img src="./media/ButtonLocked.png" width="44" height="44">

</td><td>

To edit your comment, simply double-click the **Comment** or click the **(Un)Lock** icon.

</td></tr>
<tr><td>

<img src="./media/image89.png" width="44" height="44">

</td><td>

Click this icon to **hide comment line**. This feature is especially useful in the 2D environment.

</td></tr>
<tr><td>

<img src="./media/ButtonShow.png" width="44" height="44">

</td><td>

To **hide** a comment, toggle this icon.

</td></tr>
</table>

In the **Files** tab, the comment name matches its content. You can modify comments either by changing the name in the **Files** tab or by using the **(Un)Lock** button. The first option is useful for short comments, while the second opens an editor panel at the bottom of the right-hand panel:

<img src="./media/Comment.png" width="300" height="">

This is ideal for editing longer comments that are too long for the name field.

Once you have finished editing the comment in the panel, apply the changes by clicking **Apply text**.

---

# UDiTH App — Drawing

# Drawing

<table>
<tr><td>

<img src="./media/Drawings_image56.jpg" width="44" height="44">

</td><td>

UDiTH can automatically create a technical **Drawing** from the 3D model.

</td></tr>
</table>

## Create Drawing

1. Select an object.

2. Go to the **Files** tab and select **Drawing** from the **Add** drop-down menu.

<img src="./media/FilesAddDrawing.png" width="250">

3. The name you define for the drawing, for example `DrawingXYZ`, is displayed within the drawing if a text field in the drawing border file (`.svg`) is marked as `###DRAWINGNAME###`.

4. After choosing the name, select the template you want to use. The list shows all templates defined in `drawingtemplates.json`.

<img src="./media/Drawing.png" width="255" height="">

* If the **volume clipping tool** is used, the drawing creation process takes the clipping into account and draws only the parts inside the specified volume.

* You can create a drawing of the selected 3D objects only by using the **Draw selected objects only** checkbox.

* The **Use colours** checkbox draws the objects using the same colours as in the 3D model instead of creating them in black and white.

The images below show an example of an isometric drawing created in the UDiTH Demoplant using **volume clipping** and the **Draw selected objects only** option.

<img src="./media/Drawings_image240.png" width="300" height="348">
<img src="./media/Drawings_image241.png" width="450" height="321">

## Viewing the Drawing

<table>
<tr><td>

<img src="./media/Drawings_image26.png" width="44" height="44">

</td><td>

Drawings can be opened by clicking the **Open** button next to the drawing in the **Files** tab.

</td></tr>
</table>

## Saving the Drawing
**(licence for SimpleDrawingModule is needed)**

<table>
<tr><td>

<img src="./media/ButtonSave.png" width="44" height="44">

</td><td>

To **save** any work on drawings, a licence is required. Drawings can be saved as **.svg**, **.pdf**, **.acad.pdf**, and **.png** files.

</td></tr>
</table>

> `*.acad.pdf` files are the recommended file type for AutoCAD imports if standard `*.pdf` files do not produce satisfactory results.

---

# UDiTH App — Markup

# Markup

<table>
<tr><td>

<img src="./media/ButtonMarkup.png" width="44" height="44">

</td><td>

The **Markup** tool can be used to create editable screenshots in UDiTH. When you add a Markup, a screenshot is taken from the current camera position.

</td></tr>
</table>

1. Go to the **Files** tab and select **Markup** from the **Add** drop-down menu.

<img src="./media/FilesAddMarkup.png" width="250" height="">

2. Enter a name for the **Markup**.

3. Click the **(Un)Lock** button next to the Markup to open the editing tools.

<img src="./media/MarkupUnLock.png" width="250" height="">

4. The **Markup** editing tools are available in the additional context menu on the right:

<table>
<tr><td>

<img src="./media/ButtonPhoto.png" width="44" height="44">

</td><td>

**Show initial screenshot** displays the view that was captured when the Markup was created, without any user interface elements.

</td></tr>

<tr><td>

<img src="./media/image229.png" width="75" height="44">

</td><td>

**Undo/Redo** undoes a single action or restores it if it was undone.

</td></tr>
<tr><td>

<img src="./media/IntelliPID_image96.png" width="44" height="44"></th><td>

**Draw free line** creates a freehand line that follows the movement of the mouse or finger in any direction.

</td></tr>
<tr><td>

<img src="./media/IntelliPID_image97.png" width="44" height="44"></th><td>

**Draw straight line** creates a straight line in the direction of the mouse or finger movement.

</td></tr>
<tr><td>

<img src="./media/IntelliPID_image98.png" width="44" height="44"></th><td>

**Draw orthogonal line** creates an orthogonal line aligned with the X and Y axes.

</td></tr>
<tr><td>

<img src="./media/IntelliPID_image99.png" width="44" height="44"></th><td>

**Draw rectangle** creates a rectangle whose size is defined by the mouse or finger movement.

</td></tr>
<tr><td>

<img src="./media/IntelliPID_image100.png" width="44" height="44"></th><td>

**Draw oval** creates an oval whose size is defined by the mouse or finger movement.

</td></tr>
<tr><td>

<img src="./media/IntelliPID_image101.png" width="44" height="44"></th><td>

**Draw text** creates a text field at the position where you click. The font size and colour are defined by the pen settings. If you select existing text, it can be edited.

</td></tr>
<tr><td>

<img src="./media/IntelliPID_image102.png" width="44" height="44"></th><td>

**Move** allows you to move the currently selected element in any direction on the P&ID.

</td></tr>
<tr><td>

<img src="./media/IntelliPID_image103.png" width="44" height="44"></th><td>

**Eraser** removes parts of positioned markups graphically. It does not delete entire objects, only the parts over which the tool is moved, similar to using a sponge.

</td></tr>
</table>

<table>
<tr><td>

<img src="./media/MarkupToolsPen.png" width="245" height="">

</td><td>

**Pen templates** set the drawing pen to a fixed size and colour. The last template is opaque.

</td></tr>
<tr><td>

<img src="./media/MarkupToolsSizeColor.png" width="245" height="">

</td><td>

**Pen definition** changes the size and colour of the pen.

</td></tr>
<tr><td>

<img src="./media/ButtonColourPalett.png" width="245" height="130">

</td><td>

**Pen colour** - Click the colour panel to open additional colour options. The slider controls the transparency.

</td></tr>
</table>

---

# UDiTH App — Package

# Package

Packaging allows you to include or exclude model objects and save the selection in an **.xlsx** or **.json** file. This is useful when exporting different versions of a model for different stakeholders.

<img src="./media/FilesAddPackage.png" width="250" height="">

### Creating and Loading Folders/Packages

<table>
<tr><td rowspan="2"><img src="./media/ButtonAddPackage.png" width="44" height="44"><img src="./media/image82.jpg" width="44" height="44"><img src="./media/ButtonImportObjects.png" width="44" height="44">

</td><td>

Load or create a new folder or package by clicking the relevant icon. You then gain access to the **Definition** area, where you can add definitions and connect them using logical operators.

</td></tr>
<tr><td>

If a folder or package already exists before you load another one, you can either **replace** the existing folders or packages or **expand** them.

</td></tr>
</table>

> If you try to load a package that does not contain any conditions, loading is cancelled and an error message is shown. If you accidentally saved a folder that contains empty packages, open the `.xlsx` or `.json` file and delete the empty package manually.

<img src="./media/image86.png" width="550" height="157"> <img src="./media/image87.png" width="150" height="">

### Deleting and Saving Folders/Packages

<table>
<tr><td>

<img src="./media/ButtonDelete.png" width="44" height="44">

</td><td>

Delete a folder or package by selecting it and then clicking the **Delete** icon. You are asked to confirm that you want to delete the folder together with all its contents.

</td></tr>
<tr><td>

<img src="./media/ButtonExportObjects.png" width="44" height="44">

</td><td>

Clicking the **Save** icon always saves the entire folder structure, regardless of what is currently selected. A message appears if there are any empty definition groups.

</td></tr>
</table>

### Organising Folders / Packages

> You can drag folders/packages to the end of the list and into/out of folders.

### Visualisation

> After selecting a package, you can display only its elements or grey out the rest.

## Defining Packages

Start by loading or creating a new package. You then gain access to the "Definition" area, where you can add definitions and connect them using logical operators such as **AND NOT**, **OR**, and **AND**.

<table>
<tr><td Colspan="2">Options

</td></tr>
<tr><th>**Condition by attribute**</th><th>**Condition by volume**</th></tr>
<tr><td>

-Equal

</td><td>

-Includes

</td></tr>
<tr><td>

-Not equal

</td><td>

-Overlaps

</td></tr>
<tr><td>

-Like (wildcards)

</td><td>

-NotOverlaps

</td></tr>
<tr><td>

-Not like (wildcards)

</td><td>

-NotIncludes

</td></tr>
</table>

After selecting a package, you have access to the tools for defining conditions:

### Condition by Volume

You can choose which area defined by the volume you want to include by clicking the downward arrow for the selected condition:

#### Includes / NotIncludes

<table>
<tr><td>

<img src="./media/image88.jpg" width="44" height="44">

</td><td>

Define a volume in the same way as for volume clipping. All objects that are completely inside, or respectively outside, this volume become part of the package.

</td></tr>
</table>

#### Overlaps/NotOverlaps

Define a volume in the same way as for volume clipping. All objects that are, or are not, at least partly inside this volume become part of the package.

### Condition by Attribute

<table>
<tr><td>

<img src="./media/image89.jpg" width="44" height="44">

</td><td>

You can choose an attribute, such as approval status or area, and use a text or number string to define the objects you want to include together with a matching condition.

</td></tr>
</table>

#### Equal / Not Equal

The attribute exactly matches the string entered in the text box.

Example: `Uid = c0fc06552004` selects exactly that object with exactly that unique ID.

#### Like / Not Like

The attribute matches the string entered in the text box, including any wildcards.

-  **?** for exactly one or zero character.

-  **_** for exactly one character.

-  **\*** for any number of characters.

> **Example**: `Uid c0fc06552?` matches all objects with the unique ID `c0fc065520` to `c0fc065529` and `c0fc06552a` to `c0fc06552z`, including all valid characters.

**Using wildcards**: If you use **Equal** or **Not equal**, the characters `?` and `*` are treated literally. If you use **Like** or **Not like**, `?` stands for exactly one character and `*` stands for any number of characters. Wildcards can only be added at the beginning or end of search strings.

### Grouping Conditions

It is possible to group conditions and nest them inside one another.

<table>
<tr><td>

<img src="./media/ButtonIntelliClipping.png" width="44" height="44">

</td><td>

With an IntelliGroup, you must choose a radius. All objects that are at least partly inside that radius are included in the package. A radius of `0` means that the objects are touching an object in that group.

</td></tr>
</table>

### Example

<img src="./media/image90.png" width="531" height="392">

You have a model with a restricted area for security reasons. If you need to share the model with contractors while keeping that area hidden, you can do so by placing the restricted section in a package. This requires rules based on attributes and volumes.

<table>
<tr><th Colspan="2">If you want to exclude the selected parts of your model, you can do this by the following steps:</th></tr>
<tr><td>

<img src="./media/ButtonAddPackage.png" width="44" height="44">

</td><td>

Create a new package.

</td></tr>
<tr><td>

<img src="./media/image88.jpg" width="44" height="44">

</td><td>

Select all objects you want to exclude. In the definition area, click **Condition by volume**.

</td></tr>
</table>

Choose **Inside**

<table>
<tr><td>

<img src="./media/ButtonShow.png" width="44" height="44">

</td><td>

Clicking the highlight icon shows that the selected volume also contains some small pipes and structural parts that should not be part of the package:

</td></tr>
<tr><td>

<img src="./media/image89.jpg" width="44" height="44">

</td><td>

To exclude the structural parts, create a new condition. This time, choose **Condition by attribute**.

</td></tr>
</table>

<img src="./media/image92.png" width="450" height="">

Because the **FileName** attribute of all objects that are to be excluded contains **Structure**, select **FileName** under **Available attributes** and enter **Structure** in the text box. In this case, you can use either exact (`Structure`) or fuzzy matching.

<img src="./media/PackagesAttributes.png" width="350">

Now choose **AND NOT** as the logical connection so that all objects included by the first condition remain inside the selection, except for the objects defined by the second condition.

To exclude the two nozzles, select them and create a **Condition by volume** using **Includes**.
<img src="./media/PackagesCondition2.png" width="300">

Connect that condition with **AND NOT**:

![](./media/image95.png)

> This is only one of many possible ways to get this result.

<table>
<tr><td>

<img src="./media/ButtonExportObjects.png" width="44" height="44">

</td><td>

The last step is to save your package by clicking on the icon **Save package**. Enter a name and choose a file format.

</td></tr>
</table>

In UDiTH Builder, you can load this as a **package definition file**.

---

# UDiTH App — Photo

# Photo

> Allows the user to take photos if the current device has an available camera.

<table>
<tr><td>

<img src="./media/ButtonDelete.png" width="44" height="44">

</td><td>

Cancel or retry.

</td></tr>
<tr><td>

<img src="./media/ButtonGoToGPS.png" width="44" height="44">

</td><td>

Capture photo.

</td></tr>
<tr><td>

<img src="./media/ButtonReset.png" width="44" height="44">

</td><td>

Switch camera. This requires multiple available cameras.

</td></tr>
</table>

---

# UDiTH App — Point of Interest

# Point of Interest

With the **Point of Interest** (POI) feature, you can easily mark and find specific locations and objects in the model.

To use this feature, select **Point of Interest** from the **Add** drop-down menu in the **Files** tab, then click where you want to place the POI marker.

<img src="./media/FilesAddPointofInterest.png" width="250" height="">

> Markers can be renamed, enabled, or disabled.

<table>
<tr><td>

<img src="./media/ButtonInspect.png" width="44" height="44">

</td><td>

**View** returns the camera to the position of the POI marker.

</td></tr>
</table>

<img src="./media/PoiMarker.png" width="516" height="415">

---

# UDiTH App — Report

# Report

UDiTH includes a powerful yet easy-to-use reporting engine.
To create a report, click **Add** in the **Files** tab and then select **Report**.

<img src="./media/FilesAddReport.png" width="250" height="">

## Report Definition Dialogue

<img src="./media/ReportSelection.png" width="200" height="">

The report definition selection shows a list of predefined reports. These are defined in the Excel file **IntelliPidReportDefinition.xlsx**.

> For more details about predefined report definitions, see **Report definitions** in the **Custom settings** chapter.

When you select a report definition, the report results page opens and additional filters can be applied. Two buttons are available next to each report definition:

<table>
<tr><th><img src="./media/ButtonEdit.png" width="44" height="44"></th><td>

**Report Settings**

This opens the Report Editor and allows you to modify the predefined report definition. For more details, see **Report Editor** below.

</td></tr>
<tr><th><img src="./media/ButtonSave.png" width="44" height="44"></th><td>

**Save Report**

This opens the save dialogue directly. You can save the report results as an **.xlsx** or **.csv** file for further use outside UDiTH.

</td></tr>
</table>

> If none of the existing report definitions is suitable, click **New** to open the Report Editor.

## Report Editor

The Report Editor helps you create reports easily:

-   Select whether you want to include sketch data and attributes assigned to sketch items shown in blue.

-   Choose the scope of the report. `All elements` includes all elements in the model.

-   Select the attributes you want to include in the report by dragging them to the right.

<img src="./media/IntelliPID_image164.png" width="632" height="511">

The **Reset** button clears the **Selected attributes** table. All other settings remain unchanged.

Click **OK** to generate the report and display the **Report Results** view.

## Report Results

The report results are displayed in the **Report Results** screen. The screen is divided into the **Report Menu** on the left and the results grouped by attributes. You can select multiple results by holding down the **Ctrl** key while clicking the lines:

<img src="./media/IntelliPID_image165.png" width="652" height="280">

### Report Menu

The report menu contains functions that help you narrow down the results, locate items on the P&ID, or export the results:

<table>
<tr><th><img src="./media/IntelliPID_image166.png" width="44" height="44"></th><td>

**Reload Report** - Updates the report, for example if custom attributes have changed.

</td></tr>
<tr><th><img src="./media/ButtonFit.png" width="44" height="44"></th><td>

**Fit in model** - Highlights the selected items in the corresponding P&IDs or 3D tabs. If the P&IDs are not already open, they are opened automatically and shown in the tab bar.

</td></tr>
<tr><th><img src="./media/IntelliPID_image167.jpg" width="44" height="44"></th><td>

**Select underlying objects** - This function currently produces the same result as **Fit in model**.

</td></tr>
<tr><th><img src="./media/ButtonCopy.png" width="44" height="44"></th><td>

**Copy current report definition** - Copies the current report definition to the clipboard so that it can be saved in the report definition file. For more details, see **Report definitions** in **Custom settings files**.

</td></tr>
<tr><th><img src="./media/ButtonEdit.png" width="44" height="44"></th><td>

**Customise Report Settings** - Opens the Report Editor and allows you to modify the current report definition. For more details, see **Report Editor** above.

</td></tr>
<tr><th><img src="./media/IntelliPID_image168.jpg" width="44" height="44"></th><td>

**Toggle quick filter** - Toggles the quick filter to narrow down report results. When activated, another line is shown below the attribute names. Wildcards can be used:

</td></tr>
</table>

<img src="./media/IntelliPID_image169.png" width="559" height="53">

<table>
<tr><th></th><td>

The line consists of a filter string field and two additional buttons:

</td></tr>
<tr><th><img src="./media/IntelliPID_image170.png" width="44" height="44"></th><td>

**Hide/show exclusive clauses** - Allows you to set additional filters to show or hide exactly defined values and displays the current number of values set.

</td></tr>
<tr><th><img src="./media/IntelliPID_image61.png" width="44" height="44"></th><td>

**Clear filter** - Clears and resets all filters.

</td></tr>
<tr><th><img src="./media/IntelliPID_image62.png" width="44" height="44"></th><td>

**Filter combine mode** - If several filters are set, they are combined with an **OR** operator by default, meaning only one filter condition must be true. If you switch to **AND**, all filter conditions must be met for an item to be listed.

</td></tr>
</table>

### Right-Click Filter

To refine the results further, you can also right-click, or long-tap, an attribute value in the results list. A pop-up appears, allowing you to show or hide the selected values exclusively.

<img src="./media/IntelliPID_image63.png" width="404" height="78">

All lines with the corresponding values are then either shown or hidden in the results list.

### Custom Attribute Editing

If multiple lines are selected and a custom attribute with a CodeList is included in the report, changes can be applied in bulk to all selected items:

<img src="./media/IntelliPID_image64.png" width="382" height="228">
<img src="./media/IntelliPID_image65.png" width="382" height="228">
<img src="./media/IntelliPID_image66.png" width="382" height="228">

> For even more detailed reporting and advanced reporting features, use **CAXperts UniversalReporter**.

### Filter and Sorting

You can **sort alphabetically** by clicking an attribute. **Combine** multiple search strings with a **space**, and **exclude** search strings by using **!**.

---

# UDiTH App — Screenshot

# Screenshot

You can capture and save your current viewing perspective as a screenshot. Click **Add** in the **Files** menu and select **Screenshot**. Your current view is saved as an image, which you can rename, save, and open again later.

<table>
<tr><td>

<img src="./media/ButtonScreenshot.png" width="44" height="44">

</td><td>

Captures the current viewing perspective as a screenshot.

</td></tr>
<tr><td>

<img src="./media/ButtonSave.png" width="44" height="44">

</td><td>

Saves the screenshot to the current device.

</td></tr>
<tr><td>

<img src="./media/ButtonInspect.png" width="44" height="44">

</td><td>

Opens the screenshot for viewing.

</td></tr>
</table>

---

# UDiTH App — Sketch

# Sketch

<img src="./media/FilesAddSketch.png" width="250">

Click **Files**, then **Add**, and then select **Sketch**.

This adds a new sketch layer to your model, allowing you to place custom objects without directly changing the model itself.

You can open multiple sketch tabs, but only one sketch is displayed at a time. Click **3D** in the bottom-left corner to return to the model.

<table>
<tr><th>Button</th><th>Description</th></tr>
<tr><td>

<img src="./media/ButtonCancel.png" width="44" height="44">

</td><td>

**Delete** the selected sketch item.

</td></tr>
<tr><td>

<img src="./media/ButtonCreateLink.png" width="44" height="44"><img src="./media/image83.jpg" width="44" height="44">

</td><td>

Convert 3D view objects to sketch items, which hides the source object, or clone them, which keeps the original visible. During conversion or cloning, you are prompted to choose between **referencing** and **copying**.

- **Referencing** links the **Sketch** item to the 3D object by its ID, maintaining its appearance despite model changes.
- **Copying** creates a new independent sketch item with normal behaviour.
 

</td></tr>
<tr><td>

<img src="./media/ButtonUndo.png" width="44" height="44">

</td><td>

**Undo** the last change to the tree.

</td></tr>
<tr><td>

<img src="./media/ButtonRedo.png" width="44" height="44">

</td><td>

**Redo** the last undone change to the tree.

</td></tr>
</table>

### Adding and Editing Objects

To add an object, first select it from the menu on the right. You can then drag and drop it into position.

<img src="./media/SketchObjects.png" width="250">

There are five object categories:

* **Basic structure** (ground plate, column, steel, slab)
* **Basic shapes** (cube, vertical/horizontal equipment, sphere)
* **Piping** (pipe, valve, ball valve, strainer, flange, etc.)
* **Platform**
* **Custom objects**

You can still use the standard functions in the left-hand menu. In addition, the sketch tab menu provides further options for editing the selected object.

### Example

#### Basic Structures and Shapes

Basic structures and shapes have a default scale and editing options that depend on their function:

<table>
<tr><th>Object</th><th>Default scale</th><th>Scaling options</th><th>Property to choose from</th></tr>
<tr><td>

Ground plate

</td><td>

10 x 10 m

</td><td>

X, Y & Z

</td><td>

-

</td></tr>
<tr><td>

Column

</td><td>

0.3 x 0.3 x 3 m

</td><td>

Height only

</td><td>

Profile type

</td></tr>
<tr><td>

Steel

</td><td>

0.05 x 0.05 x X m

</td><td>

Length only

</td><td>

Profile type

</td></tr>
<tr><td>

Slab

</td><td>

1 x 1 x 0.2 m

</td><td>

X, Y & Z

</td><td>

-

</td></tr>
<tr><td>

Cube

</td><td>

1 x 1 x 1 m

</td><td>

X, Y & Z

</td><td>

-

</td></tr>
<tr><td>

Sphere

</td><td>

0.1 m

</td><td>

Z

</td><td>

-

</td></tr>
<tr><td>

Vertical / horizontal equipment

</td><td>

2 x 1 m

</td><td>

X, Y & Z

</td><td>

-

</td></tr>
<tr><td>

Pipe

</td><td>

1 m, DN 50

</td><td>

Length only

</td><td>

Length and diameter

</td></tr>
</table>

#### Scaling Objects in Dependence to Others

<table>
<tr><td>

<img src="./media/image124.png" width="104" height="113">

</td><td>

If you want an object's dimensions to match the position of another object, select the object, click the scale icon, and then click and hold one of the semi-spheres on the selected object. When you move the mouse pointer over another object in the model, a green circle with crosshairs appears. The crosshairs indicate the X, Y, and Z coordinates at which the scaling will end.

</td></tr>
</table>

### Piping

<table>
<tr><td>

<img src="./media/image125.jpg" width="44" height="44">

</td><td>

When placing a pipe, you automatically get additional options that allow you to create a pipe run and connect it to other pipes or Symbol Designer objects with piping connectors.

</td></tr>
</table>

<table>
<tr><td Rowspan="3"><img src="./media/image126.png" width="72" height="115">

</td><td>

The pipes diameter can be chosen from a drop-down list in the properties.

</td></tr>
<tr><td>

You can add a 90° angle to the end of the pipe run by dragging the half-sphere, and you can move or lengthen the pipe by dragging the coloured arrows.

</td></tr>
<tr><td>

In rotation mode, you can drag the curved arrows to change the angle of the pipe. Pressing **Ctrl** makes the pipe end point towards the mouse position.

</td></tr>
</table>

### Creating a Pipe Run 

<table>
<tr><td colspan=2>The easiest way to create a pipe run is to click to where your pipe should start.

</td></tr>
<tr><td>

<img src="./media/image127.png" width="138" height="120">

</td><td>

<img src="./media/image128.png" width="164" height="148">

</td></tr>
</table>

This adds a horizontal or vertical pipe, depending on the orientation of the surface.
By dragging one of the semi-spheres, you can continue the pipe with a 90° angle connected by a bend.

> You need enough space for a bend. Otherwise bends will be displayed in red.

<img src="./media/image129.png" width="200" height="84">

<table>
<tr><td>

<img src="./media/image130.png" width="140" height="126">

</td><td>

You can add additional pipes between bends or loop pipes back on themselves. 

</td></tr>
</table>

#### Moving and Deleting Pipes

If you select a pipe, you can move it by dragging the directional arrows.

If you want to delete a pipe, you are asked whether you want to delete only that section or all connected sections as well.

#### Adding Piping Equipment

Valves and similar items can only be added to pipes that are sketch items. Select a piece of equipment and click the part of the pipe where you want to place it. You can then move it to the required position on the pipe.

> Some pipe equipment has optional properties you can choose from.

### Platforms

<table>
<tr><td>

<img src="./media/image131.jpg" width="44" height="44">

</td><td>

Start by placing the platform. Click the **Platform** icon and then click any location in the model, or drag the button into the 3D space.

</td></tr>
</table>
 
<img src="./media/image132.png" width="165" height="240"> 
To select the whole platform, select any part of it and then click that selected part again.

The Platform can be moved and scaled as a whole, but its columns and slabs can also be moved, scaled, and changed independently.

<img src="./media/image133.png" width="271" height="88">

The platform attributes include buttons for increasing or decreasing the number of columns and rows. Deleted platform objects can be reactivated with the **Activate** button. You cannot reactivate individual objects, so unnecessary parts must be deleted again.

### Custom Objects

Custom objects in the **.obj** format can be placed like other **Sketch** items. Click the button and select the object to be created in the file browser.

### Adding Objects from the Catalogue

When loading a model, UDiTH looks for a **Catalogue** folder with subfolders for objects, for example `Architectural`. The **Catalogue** folder must be in the same location as the **Data** folder.

<img src="./media/SketchObjectsCatalogue.png" width="200" height="">

> The 3D **Catalogue** in UDiTH also supports **.obj** (Wavefront) files.

The UDiTH **Catalogue** includes **.obj** or **.xls** files with corresponding **.jpg** or **.png** images. The **UDiTHsketchitemindex.txt** list file, located in the model's **Data** folder, contains all available items. The default **Catalogue** includes common plant parts such as pumps and vessels, which are available as regular objects. Although objects can be scaled, only parametric symbols retain the correct proportions.

#### Adding Objects to the Catalogue

To expand the UDiTH **Catalogue**, add any **.obj** file together with an optional **.jpg** or **.png** image. UDiTH automatically includes these items when the model is loaded, provided they are valid and correctly listed in **UDiTHsketchitemindex.txt**. Both absolute and relative paths are supported, including **#include**.

Example:

Catalogue/Ladder/ladder1.obj

/Ladder/ladder2.obj

\#include D:\\yourindex.txt

Using **#include**, objects can be stored in multiple locations as required. The included text file works in the same way as **UDiTHsketchitemindex.txt** and can also contain additional includes.

The creation of a **UDiTHsketchitemindex.txt** file can be automated by
creating a **.bat** file with the following lines of code:

\@echo off

for /f \"delims=\" %%i in (\'CD\') Do set wdrive=%%i

Setlocal EnableDelayedExpansion

if exist UDiTHsketchitemindex.txt del UDiTHsketchitemindex.txt

for /r %%a in (\*.obj,\*.xlsx) do (

SET out=%%a

rem set out=!out: =%%20!

set out=!out:d:\\\_DemoPlant\\\_\_work\\20_config\\=!

rem set out=!out:\\=/!

set out=!out:%wdrive%=Catalogue!

echo !out! \>\> UDiTHsketchitemindex.txt

)

Executing this batch file in the root folder of the catalogue that contains all subfolders and objects will delete any existing UDiTHsketchitemindex.txt file and create a new one containing all \*.obj and all \*.xlsx files in all subfolders.

#### Adding Parametric Symbols to the Catalogue

Parametric symbols are created using **3D SymbolDesigner**.

These symbols can contain attributes that change the appearance of an object, allowing complex objects such as the vehicle crane shown below, with movable parts, to be designed. The white spheres in the images represent movable parts and can be dragged to change certain attributes of a symbol, for example raising the crane. For a detailed explanation of how to create symbols, please refer to the **3D SymbolDesigner** manual.

To add parametric symbols to the catalogue, add the relevant **.xlsx** file created by **SymbolDesigner** to **UDiTHsketchitemindex.txt**, for example:

<img src="./media/image135.png" width="300" height="263"> <img src="./media/image136.png" width="300" height="263">

### Converting Objects from the 3D Model

In this example, a tank is moved and connected to an existing pipeline.

<img src="./media/image139.png" width="601" height="338">

Select the tank and its nozzles and open the **Convert** menu. Click the first button, **Convert 3D object to sketch item**, and choose **Reference**, because the original tank is to be moved.

<img src="./media/image140.png" width="601" height="370">

Click **Move** and drag the red and green arrows until the tank is next to the pipeline.

<img src="./media/image141.png" width="600" height="386">

Select the pipeline you want to connect to the tank, open the **Convert** menu, and click the last button, **Convert 3D view objects to piping**.

<img src="./media/image142.png" width="545" height="445">

---

# UDiTH App — Spraying

# Spraying

<table>
<tr><td>

<img src="./media/FilesAddSpraying.png" width="250" height="">

</td><td>

<img src="./media/image247.png" width="250" height="">

</td><td>

The **Spraying** feature allows you to select segments within a **3D laser scan object** in detail and either export or remove the selected parts from the model.

</td></tr>
</table>

> The **Spraying** tool works only in the **3D laser scan** environment.

## Tools

<table>
<tr><td>

<img src="./media/ButtonVolumeClipping.png" width="44" height="44">

</td><td>

**Volume clipping** generates a clipped area from the previously sprayed and selected surfaces.

</td></tr>
<tr><td>

<img src="./media/Spraying.png" width="200" height="">

</td><td>

**Pen** sets the drawing pen to a fixed **radius** and colour. You can also define a maximum **distance**.

</td></tr>
</table>

Press **Shift** or **Alt** to switch between drawing and removing.

> The **Spraying** tool is intended for keyboard users only. Touch screens are not supported.

You can save the sprayed region as a **.upvc** file. This file can then be used in the [**LaserscanClippingPlugin**](https://help.caxperts.com/UDiTH%20Capture%20Station/LaserscanClippingPlugin).

---

# UDiTH App — View

# View

> Views can be saved via **File management > Save configuration**. If you do not save your views, they will be lost when UDiTH is closed or when you click **Reset view**.

<table>
<tr><td>

<img src="./media/ButtonShow.png" width="44" height="44">

</td><td>

Clicking the **Views** icon opens the views panel in the menu on the right-hand side. There, you can save, delete, and edit views for the current session.

</td></tr>
</table>
<img src="./media/FilesAddView.png" width="250" height="">

When saving a view, you can enter a name and choose which information to include:

<table>
<tr><td Rowspan= 5><img src="./media/SaveView.png" width="200" height="">

</td><td>

-   Colours and visibility
-   Position
-   Highlighting
-   Clipping
-   Active aspects
-   Day/night cycle

</td></tr>
</table>

> You can reset the view, including all colours and other settings, by clicking **Reset view** in the **Camera mode** menu.

---

# UDiTH App — Settings

# Settings

## Settings

<table>
<tr><td>

<img src="./media/ButtonSettings.png" width="44" height="44">

</td><td>

UDiTH **Settings** offer a wide range of configuration options and allow you to customise many aspects of the application.

</td></tr>
</table>

#### 3D Layers

<img src="./media/Settings3Dlayers.png" width="350" height="">

You can show or hide selected objects.

#### Attribute Options

<img src="./media/SettingsAttributeOptions.png" width="350" height="">

1. **Empty attributes** shows or hides attributes that have no values.
2. **Configure priority attributes** determines which attributes are shown first, and in which order, when you hover over an object.

<img src="./media/image164.png" width="" height="">

3. **Preview attribute limit** determines how many attributes can be previewed when hovering over a 3D object.
4. **Limit for automatic labelling** determines how many labels can be shown at the same time in the 3D view.

#### Units

<img src="./media/SettingsUnits.png" width="350" height="">

Choose the unit to be displayed when measuring or adding objects.

> This does not apply to attribute information.

#### 3D User Interface
**(navigation and drawings)**

<img src="./media/Settings3Duserinterface.png" width="350" height="">

- **Common views cube** shows or hides the navigation cube in the upper-right corner of the 3D view.
- **Minimap** shows or hides the minimap in the bottom-right corner of the 3D view.
- **2-sided ISO perspective** determines the side towards which 2-sided ISOs face.
- **Use Common Views XYZ-Mode** changes the view cube labels from compass directions, such as North and South, to Cartesian XYZ labels.
- **360° Panorama** shows or hides the red spheres that indicate panorama positions.

##### UI Options

You can adjust the scale of the user interface from `0.6` to `2.0` and change the attribute font size from `2` to `6`.

> Changes to these settings take effect immediately, but you must save them in a configuration file if you want to keep them after closing UDiTH.

#### 3D Rendering

<img src="./media/Settings3Drendering.png" width="350" height="">

Option height map for **Texture mode** (Normal, Height Map, No Texture) creates a height map for the 3D model and colours objects accordingly.

<img src="./media/image167.png" width="431" height="450">

**Use camera position as light source** works similarly to a flashlight.

**Use old camera rotation behaviour** changes the camera behaviour back to that of older UDiTH versions, rotating around the centre of the screen instead of around an object.

**Hide movement indicators** hides the spheres that show the point around which the camera is rotating.

#### Color Options

<img src="./media/Settingscoloroptions.png" width="350" height="">

Here you can change the colours or patterns of the specified objects.

#### Day and Night Cycle

<img src="./media/SettingsDayandnightcycle.png" width="350" height="">

<img src="./Media/Icons_Numbers/01.png" width="16" height="16"> UDiTH includes a day/night cycle with shadow simulation. You can decide at which time the simulation starts.

<img src="./Media/Icons_Numbers/02.png" width="16" height="16"> The time multiplier increases the speed at which time passes in the 3D view.

#### Expert Settings

Advanced users can adjust the cache size, the maximum number of triangles, and the maximum camera range to suit their needs.

<img src="./media/SettingsExpert.png" width="350" height="">

<table>
<tr><td>

<img src="./media/Icons_Numbers/01.png" width="16" height="16"> **Triangles limit**

</td><td>

Limits the maximum number of triangles the system can load.

</td></tr>
<tr><td>

<img src="./media/Icons_Numbers/02.png" width="16" height="16"> **Render quality level**

</td><td>

**Low:** 30 FPS. **Medium:** 30 FPS with ambient occlusion. **High:** 60 FPS with ambient occlusion and shadows.

</td></tr>
<tr><td>

<img src="./media/Icons_Numbers/03.png" width="16" height="16"> **Memory mesh cache size (triangles)**

</td><td>

Limits the model cache size by the number of accessible **triangles**.

</td></tr>
<tr><td>

<img src="./media/Icons_Numbers/04.png" width="16" height="16"> **Memory model cache size (bytes)**

</td><td>

Limits the model cache size by the number of accessible **bytes**.

</td></tr>
<tr><td>

<img src="./media/Icons_Numbers/05.png" width="16" height="16"> **PDF projection max render jobs**

</td><td>

This setting refers to PDF rendering while zooming in and out. It limits the number of parallel rendering jobs or panels.

</td></tr>
</table>

#### About

<img src="./media/SettingsAbout.png" width="350" height="">

<table>
<tr><td>

**Show licences:**

</td><td>

Opens a new tab with legal information about the software and licences used in UDiTH.

</td></tr>
<tr><td>

**Viewer version:**

</td><td>

Shows the version of UDiTH you are using.

</td></tr>
<tr><td>

**Model version:**

</td><td>

Shows the version of the model you are using.

</td></tr>
<tr><td>

**Builder version:**

</td><td>

Shows the version of UDiTH Builder the model was created with.

</td></tr>
<tr><td>

**Conversion date:**

</td><td>

Shows the conversion date of the model.

</td></tr>
<tr><td>

**Expiration date:**

</td><td>

Shows the date until you can use the current model.

</td></tr>
</table>

### IntelliPID Settings

#### Attribute Options
<img src="./media/IntelliPidLabels.png" width="350" height="">
                    

#### Automatic Labelling Settings

<img src="./media/SettingsAutomaticLabelling.png" width="350" height="">

#### Priority Attributes Settings

Priority attributes are always displayed first in the attributes panel. This does not affect custom attributes, which are always displayed directly after the attribute to which they are attached by definition. If the item under the mouse pointer does not contain one of the selected priority attributes, the next available attribute is displayed instead.

<img src="./media/SettingsPriotityAttributes.png" width="350" height="">

### General Information

<table>
<tr><th colspan="2">Colour settings</th></tr>
<tr><td>

Highlight

</td><td>

Changes the highlight colour of selected elements.

</td></tr>
<tr><td>

Selected elements

</td><td>

Changes the colour of selected elements.

</td></tr>
<tr><td>

Sheet background

</td><td>

Changes the P&ID background colour.

</td></tr>
<tr><td>

Background elements

</td><td>

Changes the colour of non-highlighted elements.

</td></tr>
<tr><th Colspan="2">General settings</th></tr>
<tr><td>

Text offset

</td><td>

Changes the distance of the element-descriptions to the elements themselves.

</td></tr>
<tr><td>

Select attributes for automatic labelling

</td><td>

Opens a selection window for choosing attributes for automatic labelling. To select an attribute, drag and drop it into the list on the right.

</td></tr>
<tr><td>

Configure priority attributes

</td><td>

Opens a selection window for choosing priority attributes. To select an attribute, drag and drop it into the list on the right.

</td></tr>
<tr><td>

Preview attribute limit

</td><td>

Defines the maximum number of attributes displayed in the tooltip that appears when you hover over an item. Attributes that are not available are ignored.

</td></tr>
<tr><th Colspan="2">Legend Font size</th></tr>
<tr><td>

Font size

</td><td>

Changes the font size used in the P&ID legend.

</td></tr>
<tr><th Colspan="2">UI options</th></tr>
<tr><td>

UI scale

</td><td>

Changes the scale at which all user interface (UI) elements are displayed.

</td></tr>
<tr><td>

Reset UI scale

</td><td>

Resets the UI scale to normal (factor 1).

</td></tr>
<tr><td>

Attribute font size

</td><td>

Changes the font size used for attributes in the panel on the right.

</td></tr>
</table>   

### GPS Instructions

To make GPS features work correctly, you need to add calibration data to the default configuration.

Use the template below and place it in the **CommonSettings** section:

```
"CommonSettings": [
  {
    "GpsCalibration":{
      "Latitude": 48.234706,
      "Longitude": 11.679748,
      "Altitude": 500.1,
      "Bearing": 17,
      "ModelLocation": {
        "x": 3005.50,
        "y": 2385.32,
        "z": 67.16
      }
    }
  }
],
```

> If you already have a **CommonSettings** section, you can simply add it there.
> If you add it at the end of the section, ensure that the previous entry ends with a comma.

You must change the following values to match your plant:

1. **Latitude** and **Longitude** refer to the real-world coordinates of a plant reference point. You can determine these in Google Earth by placing a placemark at a known location.

<img src="./media/GoogleEarth1.png" width="500" height="133">

2. **Bearing** indicates the deviation of your plant from true north. You can determine this in Google Earth by using the measurement function to draw a line in the direction of plant north. The resulting **Heading** value can be used as the bearing.

<img src="./media/GoogleEarth2.png" width="300" height="227">

3. **ModelLocation** refers to the model coordinates of your reference point.
Use those model coordinates directly for **ModelLocation**.

<img src="./media/ModelLocation.png" width="250" height="">

> GPS altitude data is not sufficiently accurate, so instead of using it directly, add `1.5` metres to the Z coordinate.

---

# UDiTH App — Advanced options

# Advanced Options

## Output settings in UDiTH Builder

When you prepare a model in **UDiTH Builder**, you can allow or prohibit downloads in the **Output configuration** and set the default type of **Common views cube** display, either directional or XYZ.

You can also enter an **expiration date** for your model. This must not be a date in the past. Once the model has expired, it can no longer be opened.

You can also include an optional **colour configuration file** (`.xlsx`) to be loaded by default, as well as a **colour files directory** that opens when you click **File management > Load a colour file**.

In the same way, you can include a link mapping file and an attribute mapping file, which defines links between the P&ID and the 3D model.

## Set a Password for Your Model

<table>
<tr><td rowspan="2"><img src="./media/image189.png" width="194" height="126">

</td><td>

If you are providing a model using HTTPS, you can **protect access** to your model by using **HTTP Basic Authentication** with a user name and password.

</td></tr>
<tr><td>

**Windows authentication** is also supported.

</td></tr>
</table>

## Performance Settings

In the settings, you can change the triangles limit, the memory model cache size in bytes, and the memory mesh cache size in triangles, as shown below:

<img src="./media/SettingsExpert.png" width="350" height="">

<img src="./media/Icons_Numbers/01.png" width="16" height="16"> **Triangles limit** - the number of triangles from which your 3D model is built. UDiTH uses more triangles for objects closer to the camera, so objects in the distance may appear less detailed or may be missing parts.

<img src="./media/Icons_Numbers/02.png" width="16" height="16"> **Render quality level** 

-   *Low*: shading and ambient lighting are disabled.
-   *Medium*: only ambient lighting is disabled.
-   *High*: all quality settings are enabled.

<img src="./media/Icons_Numbers/03.png" width="16" height="16"> **Memory mesh cache size (triangles)** specifies the number of triangles the model is allowed to use for caching.

<img src="./media/Icons_Numbers/04.png" width="16" height="16"> **Memory model cache size (bytes)** determines the amount of memory reserved for caching your model.

<img src="./media/Icons_Numbers/05.png" width="16" height="16"> **PDF projection max render jobs** limits the rendered area of a PDF projection. This is mainly important when several PDFs are used.
   

You can add objects to the **Catalogue** under **Additional items**. To do this, place your **.obj** files, together with an optional preview **.jpg** image at `64 x 64` pixels, in the `Catalogue` folder in the same location as `UDiTH.exe`.

Add the text file `UDiTHsketchitemindex.txt`, containing the file paths to all objects, to the same folder.

For example, the file may contain:

```
Catalogue\\HeatExchanger\\Type 1.obj

Catalogue\\Pumps\\Type 1.obj

Catalogue\\VerticalVessel\\12m2.obj
```

## Adding Predefined Clippings

If you have saved a clipping to the clipboard, you can store it in an Excel file:

<table>
<tr><th> </th><th>minY</th><th>minX</th><th>minZ</th><th>maxY</th><th>maxX</th><th>maxZ</th></tr>
<tr><td>

NewVolume

</td><td>

3023.24

</td><td>

2375.12

</td><td>

67.19

</td><td>

3037.24

</td><td>

2393.118

</td><td>

72.14

</td></tr>
</table>

Save this file as `Volumes.xlsx` and place it in the data folder next to `UDiTH.exe` to make it available in **Select clipping mode**.

## Starting with a Splash Screen

During start-up, UDiTH looks for `projectMessage.png` or `projectMessage.jpg` in the data folder. If either file is found, it is displayed automatically. Users must then click **Cancel** or **Continue**. This is useful for displaying legal text, a welcome screen, or additional information.

---

# UDiTH App — IntelliPID Module

# IntelliPID Module

**IntelliPID** is a module for **UDiTH** (Universal Digital Twin Hub) that displays P&ID content, including attributes and project hierarchy. The content is created in two steps:

- Step 1 - create the P&ID input for UDiTH (`.upvp` file)

  - Export data from Smart P&ID with CAXperts [**AdapterForSpid**](https://help.caxperts.com/UDiTH%20Capture%20Station/AdapterForSpid)

  - Convert PDF P&IDs with CAXperts [**AdapterForPdfPid**](https://help.caxperts.com/UDiTH%20Capture%20Station/AdapterForPdfPid)

- Step 2 - create a session with [**UDiTH Builder**](https://help.caxperts.com/UDiTH%20Capture%20Station/Builder)

  - This can include P&ID information only
  - Or a full 3D model with advanced linking between the P&ID and the 3D model

## User interface

<img src="./media/IntelliPID3_image2.png" width=700 height=270>

The IntelliPID module interface is divided into the following areas:

- The central P&ID viewport where the P&IDs are displayed.

- The breadcrumb hierarchy (red), which is used to navigate the P&IDs by hierarchy.

- The left menu bar (blue), attached to the left edge of the screen.

- The right panel (green), which shows the Files panel, Attributes panel, or Advanced search. These panels can be rearranged and resized through the multi-window functionality.

- The tab bar (orange) at the bottom of the screen, showing 3D, open P&IDs, or documents.

More details about these areas and their integrated functions are provided in the following chapters.

## Left menu

The left menu is the main menu for UDiTH and provides access to the central functions available from all views.

## Search in Current IntelliPID
<img src="./media/IntelliPID3_image5.png" width=400 height=44>

<table>
<tr><td>

<img src="./media/IntelliPID3_image6.png" width="44" height="44">

</td><td>

The **Search in IntelliPID** button opens a search field, shown above, that searches the currently displayed P&ID for the entered string. The number of matches is shown to the right of the search string.
All matches containing the requested string are included. Wildcards are not supported.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image7.png" width="44" height="44">

</td><td>

The arrows to the right of the match count are used to navigate between the matches. The down arrow jumps to the previous match.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image7a.png" width="44" height="44">

</td><td>

The up arrow jumps to the next match.

</td></tr>
</table>

> This search function searches the _currently active_ P&ID **only**. For a global search, use [**Advanced search**](#advanced-search).

### Zoom and Control
<!-- |<img src="./media/IntelliPID3_image9.png" width="44" height="44"> | **Zoom by rectangle** will zoom the screen to the marked region on P&ID. -->
<table>
<tr><td>

<img src="./media/IntelliPID3_image8.png" width="44" height="44">

</td><td>

**Pan Mode** activates the panning function.
Alternatively, press and hold the middle mouse button, press and hold the mouse wheel, or, if neither is available, press the left and right mouse buttons simultaneously.
On mobile devices, swipe the screen with two fingers to pan.

</td></tr>
<tr><td>

</td><td>

**Zoom in or out** by using the mouse wheel or by pressing and holding the right mouse button and moving the mouse up or down.
On a touch screen, pinch apart to zoom in and pinch together to zoom out.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image9.png" width="44" height="44">

</td><td>

**Zoom by rectangle** zooms the screen to the marked region on the P&ID.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image10.png" width="44" height="44">

</td><td>

**Fit Page** centres the screen on the selected elements and zooms to fit them into the viewport. If no object is selected, the current P&ID is zoomed to fit into the viewport.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image11.png" width="44" height="44">

</td><td>

**Refresh Graphics** resets the current P&ID in the viewport to its original state.

</td></tr>
</table>

### Colourise Selected Objects
<table>
<tr><td>

<img src="./media/IntelliPID3_image15.png" width="44" height="44">

</td><td>

Users can change the display colour of selected objects by opening the colour menu. This menu is divided into four main sections, as shown below:

<img src="./media/IntelliPID3_image16.png" width=350 height=250>

</td></tr>
</table>

#### Colour Options
<table>
<tr><td>

<img src="./media/IntelliPID3_image17.png" width="44" height="44">

</td><td>

**Advanced colour selection** opens a pop-up menu for detailed colour selection, allowing the user to choose from the full RGB colour spectrum.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image18.png" width="44" height="44">

</td><td>

The **colour picker** allows the user to pick a colour from a 2D object and save it to **Custom colours**.
This can then be selected from the colour palette just like the standard colours.
When this feature is enabled, first click the object from which you want to take the colour, then select a custom colour square to assign the selected colour to it.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image19.png" width="44" height="44">

</td><td>

The **Clear style** button restores the assigned colour of the selected objects to the default colour defined when the model was created.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image20.png" width="44" height="44">

</td><td>

The **Reset all colours** button restores **all** assigned colours for **all** objects to the default colours defined when the model was created.

</td></tr>
</table>

#### Default Colours

The **Default colours** section provides a basic set of predefined colours that can be applied to the currently selected objects.

#### Custom Colours

Custom colours are defined by the user using the **Colour picker** function described above. Once defined, they can be used for colouring just like the standard colours.

#### Opacity Setting

The slider at the bottom changes the opacity. The leftmost position is fully transparent, while the rightmost position makes the selected objects fully opaque.

### P&ID navigation
<table>
<tr><td>

<img src="./media/IntelliPID3_image25.png" width="44" height="44">

</td><td>

**Go to P&ID** &ndash; When an OPC (off-page connector) is selected, clicking this button opens the linked P&ID or PIDSketch loaded in the Files tree.
The view then zooms to the corresponding OPC on the linked P&ID or PIDSketch.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image26.png" width="44" height="44">

</td><td>

**Close all IntelliPID tabs** &ndash; Closes all currently open P&IDs and removes them from the tab bar. All unsaved PIDSketches, markups, and colouring are discarded.

</td></tr>
</table>

### Tools menu
<table>
<tr><td>

<img src="./media/IntelliPID3_image27.png" width="44" height="44">

</td><td>

 The **Tools menu** contains some useful tools for visualization of IntelliPID content. When clicked, the menu opens to the right showing the contained tools:

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image28.png" width="44" height="44">

</td><td>

**Automatic labelling** &ndash; Places labels automatically on the currently displayed P&ID.
The labels are applied according to the automatic labelling settings if the objects contain one of the attributes defined in the [**Settings**](#settings) panel.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image29.png" width="44" height="44">

</td><td>

**Highlight linked objects** &ndash; Helps users identify objects that have been successfully linked to the 3D model.
For these objects, an intelligent link between the 3D model and the P&ID is active.

</td></tr>
</table>

### Custom attributes panel
<table>
<tr><td>

<img src="./media/IntelliPID3_image30.png" width="44" height="44">

</td><td>

The **Custom Attributes** button is visible only when custom attributes have been defined in a custom attributes definition file.
Further information about these files, including their creation and syntax, can be found in **Custom Attributes** in the chapter [**Custom settings**](#custom-settings).

</td></tr>
</table>

Custom attributes are user-defined attributes. They can be changed, stored, and reloaded at runtime. In the attributes panel, see [**Attributes panel**](#attributes-panel), custom attributes are displayed in yellow.

<img src="./media/IntelliPID3_image31.png" width=400 height=50>

The button opens a custom attributes panel in the right panel:

<img src="./media/IntelliPID3_image32.png" width=400 height=220>

#### Custom Attribute tool bar
<table>
<tr><td>

<img src="./media/IntelliPID3_image33.png" width="44" height="44">

</td><td>

**Export all changes** &ndash; Saves all data entered into custom attributes in a single `.xlsx` file together with the relevant information for the items to which the values are linked.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image34.png" width="44" height="44">

</td><td>

**Import all changes** &ndash; Loads previously saved custom attribute values from an `.xlsx` file back into the custom attributes.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image35.png" width="44" height="44">

</td><td>

**Send email** &ndash; Changes to the custom attributes are logged during the session. Clicking this button generates a list of changes and copies it to the clipboard.
A new email is then opened in your default email client. The default recipient and subject are defined in the `Meta` tab of `IntelliPIDattributes.xlsx`. See [**Custom settings**](#custom-settings) for more details.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image36.png" width="44" height="44">

</td><td>

**Show only active** &ndash; Custom attributes not used in the currently displayed P&ID are hidden from the list of used values for that P&ID and from the corresponding legend.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image37.png" width="44" height="44">

</td><td>

**Place legend** &ndash; Displays a legend with the corresponding custom attribute colour keys on the P&ID.
This legend can be moved by dragging it to any location on the P&ID. See the description of custom attribute CodeLists and legends below.

</td></tr>
</table>

<img src="./media/IntelliPID3_image39.png" width=400 height=180>

#### Custom Attribute Drop-down

This drop-down control allows the user to select from custom attributes with CodeList definitions. By default, none is selected, and the P&ID colouring remains unchanged.
When a CodeList custom attribute is selected, the objects in the P&ID are coloured according to their custom attribute values and the legend is displayed below.

#### Legend

The legend provides colour information for the custom attribute selected from the drop-down described above. Unused values can be hidden using **Show only active**.
The displayed values and their corresponding colours are shown in the placed legend.
The legend can be resized.

#### Settings
<table>
<tr><td>

<img src="./media/IntelliPID3_image40.png" width="44" height="44">

</td><td>

Opens the settings panel for UDiTH. Users can configure the main P&ID settings in all options containing `IntelliPID` in their description:
 <img src="./media/IntelliPID3_image41.png" width=600 height=280>

</td></tr>
</table>

<table>

<tr><th>Setting</th><th>Description</th></tr>
<tr><td>

**Color options**

</td></tr>     
<tr><td>

Highlight

</td><td>

Changes the highlight colour of selected elements.

</td></tr>   
<tr><td>

Selected elements

</td><td>

Changes the colour of selected elements.

</td></tr>   
<tr><td>

Sheet background

</td><td>

Changes the P&ID background colour.

</td></tr>   
<tr><td>

Background elements

</td><td>

Changes the colour of elements that are not linked to the 3D model.

</td></tr>   
<tr><td>

**General settings**

</td></tr> 
<tr><td>

Text offset

</td><td>

Changes the distance between element descriptions and the elements themselves.

</td></tr>   
<tr><td>

Use flat IntelliPID hierarchy

</td><td>

Changes the breadcrumb hierarchy from structured view to flat view.

</td></tr>   
<tr><td>

**Attribute options**

</td></tr>
<tr><td>

Select attributes for automatic labelling

</td><td>

Opens a selection window for choosing attributes for automatic labelling. To select an attribute, drag and drop it into the list on the right.

</td></tr>
<tr><td>

Configure priority attributes

</td><td>

Opens a selection window for choosing priority attributes. To select an attribute, drag and drop it into the list on the right.

</td></tr>
<tr><td>

Configure visible attributes

</td><td>

Defines the maximum number of attributes displayed in the tooltip that appears when you hover over an item.
Attributes that are not available are ignored.

</td></tr>
<tr><td>

**Legend Font size**

</td></tr>
<tr><td>

Font size

</td><td>

Changes the font size used in the P&ID legend.

</td></tr>
<tr><td>

Use default legend position

</td><td>

Enables the use of a default legend position, when one has been defined.

</td></tr>
<tr><td>

Use colorful legend names

</td><td>

The legend description names are coloured according to the legend content.

</td></tr>
<tr><td>

**UI options**

</td></tr>
<tr><td>

UI scale

</td><td>

Changes the scale at which all user interface (UI) elements are displayed.

</td></tr>
<tr><td>

Reset UI scale

</td><td>

Resets the UI scale to normal (factor 1).

</td></tr>
<tr><td>

Attribute font size

</td><td>

Changes the font size used for attributes in the panel on the right.

</td></tr>
<tr><td>

**P&ID Sketches**

</td></tr>
<tr><td>

Snap mode ON

</td><td>

Activates snap mode during PIDSketch mode. The snapping option can also be turned on or off using the hotkey `Ctrl+F` while PIDSketch edit mode is active.

</td></tr>
<tr><td>

**P&ID Grid options**

</td></tr>
<tr><td>

Show grid

</td><td>

Activates the grid on the IntelliPID canvas. The grid can also be turned on or off using the hotkey `Ctrl+G`.

</td></tr>
<tr><td>

Grid opacity

</td><td>

Modifies the intensity of the grid display.

</td></tr>
<tr><td>

Width value

</td><td>

Modifies the width of each cell on the grid.

</td></tr>
<tr><td>

Width unit

</td><td>

Selects the grid unit system (mm or inch).

</td></tr>
<tr><td>

Use dynamic width

</td><td>

Activates the predefined dynamic grid width. The user can select a width unit: fine, normal, or coarse.

</td></tr>
<tr><td>

**Expert Settings**

</td></tr>
<tr><td>

Align custom symbol leader lines automatically

</td><td>

Forces automatic connection of the leader line when a `connecteditemid` attribute exists.

</td></tr>
<tr><td>

Use objects center for leader line

</td><td>

Uses the centre of the object as the default connection point for the leader line of the custom symbol.

</td></tr>
</table>

#### Automatic Labelling Settings
<img src="./media/IntelliPID3_image42.png" width=400 height=350>

#### Priority Attributes Settings
Priority attributes are always displayed first in the attributes panel. This does not affect custom attributes.
Custom attributes are always displayed directly after the attribute to which they are attached by definition - see [**Custom Attributes**](#custom-attributes) in [**Custom settings**](#custom-settings).
If the item under the mouse pointer does not contain one of the selected priority attributes, the next available attribute is displayed instead.

<img src="./media/IntelliPID3_image43.png" width=400 height=350>

#### File management
<table>
<tr><td>

<img src="./media/IntelliPID3_image44.png" width="44" height="44">

</td><td>

Opens the Files panel. See [**Files panel**](#files-panel) for more details.

</td></tr>
</table>

#### Manual
<table>
<tr><td>

<img src="./media/IntelliPID3_image45.png" width="44" height="44">

</td><td>

**Open manual** &ndash; Opens the link to the manual on CAXperts Help.

</td></tr>                                       
</table>

## Right Panel
The right panel is a multi-purpose panel and its function varies depending on the context. The header section of the panel contains the following main views:

<img src="./media/IntelliPID3_image72.png" width=400 height=100>

It provides access to the following views and features, which are described in the following chapters:
- **Attributes panel**
- **Advanced search panel**
- **Files panel** (optional)

The panel tabs can be rearranged and resized through the multi-window functionality:

<img src="./media/IntelliPID3_image74.png" width=400 height=300>

### Advanced Search
In addition to **Search in Current IntelliPID**, UDiTH includes an advanced search function designed for working with both 3D models and 2D drawings. The results are displayed according to the attributes in which the string was found. The number shown after each attribute indicates the number of matching groups in that attribute, using the same colour for hard matches and grey for soft matches, as well as the number of affected P&IDs in blue. When an attribute is expanded, the matching groups are displayed as a list with the corresponding P&ID name shown in blue.

<img src="./media/IntelliPID3_image76.png" width=400 height=300>

### Attributes Panel

The **Attributes** tab is displayed by default at start-up. It contains all attribute information for the selected item or items provided during the building process, whether from the authoring system or the UDiTH SDK.

<img src="./media/IntelliPID3_image80.png" width=400 height=300>

## Files Panel
The Files panel is the central location for UDiTH files attached to the model. All sketches, markups, comments, views, and similar items are displayed in a tree structure that makes it easy to navigate through all attached files.

Although all nodes in the tree are always visible, there are some differences in file creation between 3D and IntelliPID. This chapter describes the IntelliPID-specific functions together with some general features of the Files tree. The three sections of the panel are:
- **Tool bar** &ndash; Tree editing and file operations
- **Model root** &ndash; The model root is always present and is the anchor for the tree structure.
- **Files tree** &ndash; Contains a hierarchical structure of the files attached to the model.

<img src="./media/IntelliPID3_image85.png" width=400 height=180>

### Files Tool Bar
#### File Creation
<table>
<tr><td>

<img src="./media/IntelliPID3_image86.png" width="44" height="44">

The file creation button is context-sensitive. Depending on the object selected when **Add** is clicked, different options are available. Newly created files are placed beneath the selected file in the hierarchy:

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image87.png">

</td><td>

<img src="./media/IntelliPID3_image88.png">

</td><td>

<img src="./media/IntelliPID3_image89.png">

</td></tr>
<tr><td>

**Model root is selected**

</td><td>

**Folder is selected**

</td><td>

**PIDSketch is selected**

</td></tr>
</table>

<table>
<tr><td>

<img src="./media/IntelliPID3_image90.png" width="44" height="44">

</td><td>

**Remove** &ndash; Deletes the currently selected node or object from the tree.
If the selected item is a folder, all files inside that folder are also deleted after a confirmation prompt:
<img src="./media/IntelliPID3_image91.png" width=400 height=130>

</td></tr>  
<tr><td>

<img src="./media/IntelliPID3_image92.png" width="44" height="44">

</td><td>

**Copy** &ndash; Copies the selected tree object. Works for `Folder` and `Markup`.

</td></tr> 
<tr><td>

<img src="./media/IntelliPID3_image124.png" width="44" height="44">

</td><td>

 **Undo** &ndash; Undoes the last change to the tree.

</td></tr> 
<tr><td>

<img src="./media/IntelliPID3_image125.png" width="44" height="44">

</td><td>

 **Redo** &ndash; Redoes the last undone change to the tree.

</td></tr>  
<tr><td>

<img src="./media/IntelliPID3_image94.png" width="44" height="44">

</td><td>

**Load** &ndash; Opens a file dialogue to select a UDiTH file to load.
The loaded file is appended to the Files tree hierarchy automatically. To replace the existing hierarchy, remove it first.

</td></tr> 
<tr><td>

<img src="./media/IntelliPID3_image95.png" width="44" height="44">

</td><td>

 **Save** &ndash; Opens the save options:

<img src="./media/IntelliPID3_image96.png">

</td></tr> 
</table>

#### Relevant Options for Saving in IntelliPID
<table>
<tr><td>

<img src="./media/IntelliPID3_image56.png" width="44" height="44">

</td><td>

**Save** &ndash; Tree elements can be selected individually for saving.
Disabling a folder object also disables all objects within that folder hierarchy.
Clicking **Save** opens a save dialogue in which you can choose the file name and location. The designated file type is `*.upvf`.

</td></tr> 
<tr><td>

<img src="./media/IntelliPID3_image33.png" width="44" height="44">

</td><td>

**Save config** &ndash; Saves the configuration to a UDiTH file. A configuration includes the following areas:

<img src="./media/IntelliPID3_image99.png" width=200 height=180>

</td></tr> 
<tr><td>

<img src="./media/IntelliPID3_image100.png" width="44" height="44">

</td><td>

 **Export to PDF** &ndash; Opens a file save dialogue for saving IntelliPID P&IDs in PDF format. In order to export searchable PDFs (Windows only), it is necessary to have installed **Inkscape 1.4.x** on the same device. 
 Several PDF export options are available:

<img src="./media/IntelliPID3_image101.png" width=400 height=350>

</td></tr> 
<tr><td>

                                                                   

</td><td>

 **As shown** &ndash; The P&ID will be exported as displayed, including selection highlighting and display styles.

</td></tr> 
<tr><td>

                                                                   

</td><td>

 **As modified** &ndash; The P&ID will be exported as displayed, selection highlighting and display style are ignored.

</td></tr> 
<tr><td>

                                                                   

</td><td>

 **Original** &ndash;  The P&ID will be exported in its original state, all coloring and sketching are ignored.

</td></tr>   
<tr><td>

                                                                   

</td><td>

 A P&ID name filter can be applied to the list of available P&IDs. Users get to select from this list using the following selection and export options:

</td></tr>   
<tr><td>

                                                                   

</td><td>

 **Manual selection** &ndash; Select P&IDs by clicking on the check box.

</td></tr> 
<tr><td>

                                                                   

</td><td>

 **With Sketches** &ndash; Selects all P&IDs in the project that have applied PIDSketches.

</td></tr> 
<tr><td>

                                                                   

</td><td>

 **Select current** &ndash; SSelects only the currently displayed P&ID.

</td></tr> 
<tr><td>

                                                                   

</td><td>

 **Select open** &ndash; Selects all P&IDs currently open (visible in the tab bar on the bottom).

</td></tr>
<tr><td>

                                                                   

</td><td>

 **Select all** &ndash; Selects all P&IDs in the project.

</td></tr> 
<tr><td>

                                                                   

</td><td>

 **Zip file**  Exports selected P&IDs to PDF in one zip file.

</td></tr> 
<tr><td>

                                                                   

</td><td>

 **Concat into single file** &ndash; Exports selected P&IDs into one single PDF binder.
 The drawing names will be exported as bookmarks to make navigation easier.

</td></tr> 
<tr><td>

<img src="./media/IntelliPID3_image102.png" width="44" height="44">

</td><td>

 **Filter selected files** &ndash; selects/unselects elements in a files tree by their type:

<img src="./media/IntelliPID3_image103.png" width=200 height=250>

</td></tr> 
<tr><td>

<img src="./media/IntelliPID3_image105.png" width="44" height="44">

</td><td>

 **Select display style** &ndash; Opens a pop-up menu for display style selection.
 The model is loaded with a default color file, but additionally, users can load coloring settings depending on attribute conditions defined in color files.

<img src="./media/IntelliPID3_image106.png" width=200 height=250> 
 <img src="./media/IntelliPID3_image107.png" width=200 height=150>

</td></tr>
</table>

Depending on the settings in the \'upvcolorindex.txt\' file, a list of custom display styles will be displayed.
Files not listed can be opened directly from the file system by clicking **Open local file**.

If a **color group** display style is selected, the user can choose which color group to enable.
In addition, it is possible to apply a single or multiple color system to P&IDs and export the P&IDs to a concatenated PDF according to their color system by clicking the 'PDF Export (Single System)' button. 

Users can create custom color files using Excel by entering a color and condition. For more details on the settings and definitions, refer to the [**Color Files**](#color-files) section of the [**Custom settings**](#custom-settings) chapter.

<table>
<tr><td>

<img src="./media/IntelliPID3_image109.png" width="44" height="44">

</td><td>

 **Load link file** &ndash; Opens a file system dialogue to load a link file (.xlsx).
 You can replace existing links or append the existing links with new ones. For more details about the formatting and definition of links, see [**Link Mapping Files**](https://help.caxperts.com/UDiTH%20App/Appendix%20-%20Custom%20settings%20files#link-mapping-files).

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image110.png" width="44" height="44">

</td><td>

 **Set current legend position as default legend position** &ndash; This option sets a current P&ID legend position and size displayed in IntelliPID as the default legend position for all other P&IDs in the current session and can be also stored to default.upvf, if required.

</td></tr>
</table>

### File and object types

In IntelliPID, there are several file types available in the Files tree:

<table>
<tr><td>

<img src="./media/IntelliPID3_image112.png" width="44" height="44">

</td><td>

 **Folder** &ndash; Adds a Folder object to the Model root. Subfolder are also supported. In folders, all other types of files can be stored in and thereby grouped.
 For multiple files to one P&ID, for example, users can create a folder that contains all relevant files for that P&ID.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image113.png" width="44" height="44">

</td><td>

 **AppControl** &ndash; Creates a new "AppControl" For more details, see the general chapter [**AppControl**](https://help.caxperts.com/UDiTH%20App/AppControls).

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image114.png" width="44" height="44">

</td><td>

 **Markup** &ndash; The markup file enables you to do markups on your P&IDs, including graphical objects. For detailed information, see the following chapter [**P&ID Markup**](#pid-markup).

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image115.png" width="44" height="44">

</td><td>

 **PIDSketch** &ndash; The sketch file enables you to add sketches to your P&ID, including you custom P&ID symbols. For detailed information, see the chapter [**P&ID Sketch**](#pid-sketch).

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image50.png" width="44" height="44">

</td><td>

 **Report** &ndash; This opens the report creation dialogue. Predefined custom and ad-hoc reports can be generated here. For more details, see the chapter [**Reporting**](#reporting).

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image116.png" width="44" height="44">

</td><td>

 **Comment** &ndash; The commenting object contains one comment per node. Comments can be attached to any P&ID element, original or sketch.
 Commenting objects are always dependencies of a P&ID Sketch object. For more details, see the Chapter [**P&ID comment**](#pid-comment).

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image120.png" width="44" height="44">

</td><td>

 **Window Layout** &ndash; Enables saving and loading of customizable layout or predefined startup views for each UDiTH instance.

</td></tr>
</table>

### Context buttons

All objects have context buttons to the right of the file/object node. Depending on the file/object type, different functions are available.
They become visible when the user moves the mouse over the corresponding node or clicks on the object.

#### Context controls
<table>
<tr><td>

<img src="./media/IntelliPID3_image116.png" width="44" height="44">

</td><td>

 **Edit** &ndash; Opens the according file in edit mode. An editor panel is shown in the right panel depending on the file type selected.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image118.png" width="44" height="44">

</td><td>

 **Show/Hide** &ndash; Shows or hides the according file/object in the viewport.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image138.png" width="44" height="44">

</td><td>

 **Show/Hide** **Comment line** &ndash; Shows or hides the leader line of the comment.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image158.png" width="44" height="44">

</td><td>

 **Select** &ndash; Selects and highlights sketch items on a selected PIDSketch. Useful for multi-sketch drawings.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image165.png" width="44" height="44">

</td><td>

 **Apply at startup** &ndash; This toggle activates automatic loading of the customized window layout on every startup of the UDiTH model.

</td></tr>
</table>

## P&ID Markup
<table>
<tr><td>

<img src="./media/IntelliPID3_image114.png" width="44" height="44">

</td><td>

 The Markup function in IntelliPID is a tool that enables the creation of graphical and textual markups for P&ID (see section [**Files tool bar**](#files-tool-bar) in the chapter [**Files panel**](#files-panel)).

</td></tr>
</table>

The P&ID becomes a virtual whiteboard on which the markup function can be used to draw without altering the underlying graphics. Several markups can be created on one drawing and visualized simultaneously. 

After assigning a name, an editor panel opens at the bottom of the right panel:

<img src="./media/IntelliPID3_image122.png" width="400" height="200">

### Tool bar
<table>
<tr><td>

<img src="./media/IntelliPID3_image123.png" width="44" height="44">

</td><td>

 **Show initial screenshot** &ndash; Shows a screenshot of the P&ID before Markup was applied.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image121.png" width="44" height="44">

</td><td>

 **Show last saved screenshot** &ndash; Shows a screenshot of the P&ID in the state when editing was started. This includes Markups made previously.

</td></tr>                    
<tr><td>

<img src="./media/IntelliPID3_image124.png" width="44" height="44">

</td><td>

 **Undo** &ndash; Undoes the last action taken in Markup.

</td></tr> 
<tr><td>

<img src="./media/IntelliPID3_image125.png" width="44" height="44">

</td><td>

 **Redo** &ndash; Redoes the last undone action.

</td></tr>
</table>

### Drawing tools
<table>
<tr><td>

<img src="./media/IntelliPID3_image126.png" width="44" height="44">

</td><td>

 **Draw free line** &ndash; Creates a freehand line following mouse or finger movement in all directions.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image127.png" width="44" height="44">

</td><td>

 **Draw straight line** &ndash; Creates a straight line in the direction of mouse or finger move.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image128.png" width="44" height="44">

</td><td>

 **Draw orthogonal line** &ndash; Creates an orthogonal line bound to X and Y axis.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image129.png" width="44" height="44">

</td><td>

 **Draw rectangle** &ndash; Draws a rectangle. Size is defined by mouse or finger movement.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image130.png" width="44" height="44">

</td><td>

 **Draw oval** &ndash; Draws an oval. Size is defined by mouse or finger movement.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image131.png" width="44" height="44">

</td><td>

 **Draw text** &ndash; Creates a text field at the position where the mouse is clicked.
 Font size and color are defined by the pen definition. If an existing text is picked, this text can be edited.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image132.png" width="44" height="44">

</td><td>

 **Move** &ndash; Enables you to move the currently selected element in any direction on the P&ID.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image133.png" width="44" height="44">

</td><td>

 **Erase** &ndash; The erase tool erases graphically the positioned markups.
 It does not delete coherent objects but rather those parts the tool is swiped over, resembling a sponge.

</td></tr>
</table>

### Pen templates
A pen template sets the drawing pen to a fixed size and color. The last template (yellow color) is transparent.

### Pen definition
The pen definition is used to set the size, color, and transparency of the pen.

## P&ID sketch

<table>
<tr><td>

<img src="./media/IntelliPID3_image115.png" width="44" height="44">

</td><td>

The P&ID Sketch feature allows users to sketch into existing P&IDs. Sketches are applied to a separate layer and do not modify the original document.</tr>
</table>

P&ID Sketching provides preset objects as well as access to a custom catalog. To create a PIDSketch, please refer to the [**Files tool bar**](#files-tool-bar) section in the [**Files panel**](#files-panel) chapter.
After assigning a name to the created PIDSketch, an editor panel opens at the bottom of the right panel:

<img src="./media/IntelliPID3_image135.png" width=400 height=150>

### Tab Selector
The Tab Selector allows users to toggle between the P&ID sketch functions, 2D catalog, sketch labels and sketch items presented on the P&ID Sketch.

### PIDSketch Name
PIDSketch name shows the name of the currently open/edited P&ID sketch and the corresponding P&ID number/title in parentheses.

### Tool bar

<table>
<tr><td>

<img src="./media/IntelliPID3_image124.png" width="44" height="44">

</td><td>

 **Undo** &ndash; Undoes the last action taken in PIDSketch.

</td></tr>                   
<tr><td>

<img src="./media/IntelliPID3_image125.png" width="44" height="44">

</td><td>

 **Redo** &ndash; Redoes the last undone action in PIDSketch.

</td></tr>                   
</table>

### Sketching tools
The Sketching Tools include the following presets and typicals for 2D sketching:

<table>
<tr><td>

<img src="./media/IntelliPID3_image137.png" width="44" height="44">

</td><td>

 **Routing** &ndash; Starts a line routing process to sketch orthogonal lines. To finish routing, click or tap the button again or use the right mouse button.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image138.png" width="44" height="44">

</td><td>

**Free Routing** &ndash; Starts a line routing process to sketch lines at a defined angle (default is 45°).
 To finish free routing, click or tap the button again or use the right mouse button. To resume free routing, hold the Ctrl button and click one end of the line.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image126.png" width="44" height="44">

</td><td>

**Free Hand** &ndash; Creates a freehand line following mouse or finger movement in all directions. To finish routing, click or tap the button again or use the right mouse button.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image127.png" width="44" height="44">

</td><td>

**Polyline** &ndash; Creates a straight line in the direction of the mouse clicks. To finish routing, click or tap the button again or use the right mouse button.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image130.png" width="44" height="44">

</td><td>

**Circle** &ndash; Places a circular typical, e.g. for pumps. When placed, the line (original and sketch) it is placed upon will be interrupted to fit in the symbol.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image129.png" width="44" height="44">

</td><td>

**Rectangle** &ndash; Places a rectangular typical, e.g. for measurements, indicators, etc.
 When placed, the line (original and sketch) it is placed upon will be interrupted to fit in the symbol.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image141.png" width="44" height="44">

</td><td>

**Valve** &ndash; Places a general valve typical. When placed, the line (original and sketch) it is placed upon will be interrupted to fit in the symbol.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image142.png" width="44" height="44">

</td><td>

**Revision Cloud** &ndash; Starts a freehand line routing process to sketch a revision cloud.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image131.png" width="44" height="44">

</td><td>

**Text** &ndash; Creates a text field at the position where the mouse is clicked. The text value, font, size and individual color can be defined in the text attributes.

</td></tr>
</table>

### Commands

<table>
<tr><td>

<img src="./media/IntelliPID3_image143.png" width="44" height="44">

</td><td>

 **Break line** &ndash; Breaks the selected line and creates a separate segment between inline components.
For breaking a straight line into two segments, an object needs to be placed first. 
 First click on the point you want the break to be placed, then click the Break line function.
 Depending on the positioning on the line the break will be applied to the closest end to clicked point: 
<img src="./media/IntelliPID3_image152.png" width="600" height="55">

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image153.png" width="44" height="44">

</td><td>

 **Un Break line** &ndash; Removes the selected line breaks.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image154.png" width="44" height="44">

</td><td>

 **Copy** &ndash; Creates a copy of the selected object (works for original and sketch items).
 The copy is attached to the mouse position and can be placed by clicking on the destination position.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image155.png" width="44" height="44">

</td><td>

 **Rotate** &ndash; Rotates or mirrors the selected sketch item or a selection of sketch items.Active rotation command is symbolized by the rotation symbol in the center of the sketch item. 
 The rotation commands are activated by clicking/tapping the respective symbol and

</td></tr>   
<tr><td>

<img src="./media/IntelliPID3_image156.png" width="44" height="44">

</td><td>

 **45° Rotation symbol** &ndash; rotates the sketch item in 45° increments. 
 **Rotation symbol** &ndash; rotates the sketch item freely. 
 **Mirror symbol** &ndash; Flips the symbol axially in the dragged direction.

</td></tr> 
<tr><td>

<img src="./media/IntelliPID3_image157.png" width="44" height="44">

</td><td>

 **Change layer color** &ndash; Opens a default color selection pop-up (see section **Colorize selected objects** in the Chapter [**Left menu**](#left-menu)).
 The color is applied to all sketch items in the current sketch layer.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image174.png" width="44" height="44">

</td><td>

 **Hide Objects** &ndash; Hides the selected objects on the drawing.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image179.png" width="44" height="44">

</td><td>

 **Show Hidden Objects** &ndash; Shows hidden objects on the drawing (from hidden layer).

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image36.png" width="44" height="44">

</td><td>

 **Unhide Objects** &ndash; Unhides all objects from hidden layer.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image175.png" width="44" height="44">

</td><td>

 **Sub Area Colorize Mode** &ndash; When activated, the existing fill color of the objects can be changed.

</td></tr>
<tr><td>

**Hotkey**

</td><td>

 **Copy & Paste** &ndash; Within UDiTH: Press "Ctrl+C" to copy elements. Press "Ctrl+V" to insert the copied elements from the clipboard into the same PIDSketch, another PIDSketch or a new PIDSketch on another drawing.

</td></tr>
<tr><td>

**Hotkey**

</td><td>

 **Snap Mode ON/OFF** &ndash; Press "Ctrl+F" to toggle snap mode on/off while being in PIDSketch edit mode.

</td></tr>
<tr><td>

**Hotkey**

</td><td>

**Show Grid ON/OFF** &ndash; Press "Ctrl+G" to show grid on/off.

</td></tr>
</table>

### Sketch attributes
Simple sketch items possess only a few attributes by default. The default attribute NAME can be directly changed in the attributes panel while PIDSketch is in edit mode. Just click into the value of the NAME attribute to change it:

<img src="./media/IntelliPID3_image162.png" width=600 height=100>

If more attributes are needed, they can be added as necessary in the lower part of the attributes:

<img src="./media/IntelliPID3_image163.png" width=600 height=100>

Enter the fields marked with "Enter text..." and type in you new attribute name on the left and the according new value on the right (green fields). To confirm the new attribute hit Enter or click on the Add Attribute button (marked red).
The new attribute will appear in the attribute list. If you wish to remove the attribute, click on "delete" symbol

><img src="./media/IntelliPID3_image90.png" width="44" height="44">

The attribute will be deleted then.

The custom and changeable attributes can be predefined in the **intelliPidAttributes.xlsx.** Please see the chapter [**Custom Attributes**](#custom-attributes).

#### Attribute "Style"
Different styles are available (depending on the sketch item) and can be selected from the attribute dropdown menu. The available styles for routing are:

<img src="./media/IntelliPID3_image178.png" width=400 height=300>

<table>
<tr><th>Example</th><th>Style</th></tr>
<tr><td>

<img src="./media/IntelliPID3_image180.png" width=100 height=15>

</td><td>

 Normal

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image181.png" width=100 height=15>

</td><td>

 Dashed

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image182.png" width=100 height=15>

</td><td>

 DashedDotLine

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image183.png" width=100 height=10>

</td><td>

 DashedDotLine2

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image184.png" width=100 height=15>

</td><td>

 FlexibleHose

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image185.png" width=100 height=30>

</td><td>

 HatchedAreaLarge

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image186.png" width=100 height=15>

</td><td>

 HatchedAreaSmall

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image187.png" width=100 height=10>

</td><td>

 HeatTracing

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image188.png" width=100 height=10>

</td><td>

 Jacketed

</td></tr>
</table>

#### Attribute "Width"
There are different width factors available for simple sketch items and copied items, which can be selected from the attribute dropdown menu called "Width".

<img src="./media/IntelliPID3_image159.png" width=600 height=300>

#### Attribute "Color"
Individual colors can be applied to selected sketch items via the color attribute. The default setting is "By Layer", indicating that the specified PIDSketch color will be applied. The "By Layer" color can be modified individually for each sketch element in the current PIDSketch:

<img src="./media/IntelliPID3_image160.png" width=400 height=400>

The individual color can be restored to its "By Layer" state by selecting the "Reset color of selected objects" button: 

><img src="./media/IntelliPID3_image19.png" width="44" height="44"> 

### Catalogue
The catalogue contains a collection of custom sketching symbols for IntelliPID sketching. It allows users to add custom P&ID sketching symbols to IntelliPID, enabling users to extend the sketching abilities and adapt sketching fully to their needs.
When loading a model, UDiTH checks for a upvintellipidsketchitems.txt file located in the \Data\ folder of the model. This file contains the linkage to all available items for the current model.
For more details about the sketch items list file and how to add your own custom symbols, see section [**Sketch item index file**](#sketch-item-index-file) in the chapter [**Custom settings**](#custom-settings-files).

<img src="./media/IntelliPID3_image164.png" width=300 height=400>

### Labels
In the tab labels, users can select predefined labels and place them on the sketching objects. The attributes of the label are inherited by the sketching objects. These label attributes can be directly modified either on the sketch item itself or on the label.

<img src="./media/IntelliPID3_image176.png" width=400 height=250>

The labels can be predefined in the **IntelliPIDattributes.xlsx** in the tab "LabelDefinition". Please see the chapter [**Worksheet "LabelDefinition**](#worksheet-labeldefinition).

### Sketch Items
In the tab sketch items all the existing sketch elements on the current PIDSketch are listed and can be also selected.

<img src="./media/IntelliPID3_image177.png" width=400 height=180>

## P&ID comment

<table>
<tr><td>

<img src="./media/IntelliPID3_image116.png" width="44" height="44">

</td><td>

 Create a comment for the currently selected P&ID object. The comment will attach to the centre of the object with a leader line. 
 The leader line can be shown or hidden respectively &ndash; for further details, see section [**Context buttons**](#context-buttons) in the chapter [**Files panel**](#files-panel).

</td></tr>
</table>

The name of the comment in the Files tree is identical to the comment content. Comments can be altered by either changing the name in the Files tree or by double click on the comment in the viewport. While the first is helpful for short comments, the editing in viewport allows multiline input.

## Reporting

UDiTH includes a powerful yet easy-to-use reporting engine. The report creation dialogue is opened by the corresponding button in the [**Files panel**](#files-panel) within the [**Right panel**](#right-panel).

### Report Creation Dialogue
<img src="./media/IntelliPID3_image54.png" width=300 height=300>

The report creation dialogue shows a list of predefined reports. These are defined in the Excel file `IntelliPidReportDefinition.xlsx`.
For more details about predefined reports, see [**Report definitions**](#report-definitions) in the chapter [**Custom settings**](#custom-settings).

Selecting a report definition opens the report results page, where additional filters can then be applied.
Two buttons are shown next to each report definition:

<table>
<tr><td>

<img src="./media/IntelliPID3_image55.png" width="44" height="44">

</td><td>

**Report Settings** &ndash; Opens the Report Editor and allows you to modify the predefined report definition.
For more details, see [**Report editor**](#report-editor) below.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image56.png" width="44" height="44">

</td><td>

**Save Report** &ndash; Opens the save dialogue directly. You can save the report result as an `.xlsx` or `.csv` file for further use outside UDiTH.

</td></tr>
</table>

If none of the available report definitions is suitable, click **New** to open the Report Editor.

### Report Editor
The Report Editor helps you create reports easily:

- Select whether you want to include sketch data and attributes assigned to sketch items.

> Currently, report generation for PIDSketch data can be performed for a maximum of the last **five** opened P&ID drawings.

- Choose the scope you want to report on. `All elements` includes all elements in the model.
- Select the attributes you want to report on by dragging them to the right.

<img src="./media/IntelliPID3_image57.png" width=400 height=300>

- The **Reset** button clears the **Selected attributes** table. All other settings remain unchanged.
- Clicking **OK** generates the report and displays the **Report Results** view.

### Report Results
The report results are displayed in the **Report Results** screen. The screen is divided into the **Report Menu** on the left and the results grouped by attributes.
You can select multiple results by holding down the **Ctrl** key while clicking the lines:

<img src="./media/IntelliPID3_image58.png" width="400" height="170"> 

#### Report Menu
The report menu contains functions that help you narrow down the results, locate items on the P&ID, or export the results:

<table>
<tr><td>

<img src="./media/IntelliPID3_image11.png" width="44" height="44">

</td><td>

**Reload Report** &ndash; Updates the report, for example if custom attributes have changed.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image10.png" width="44" height="44">

</td><td>

**View in model** &ndash; Highlights the selected items in the corresponding P&IDs. If the P&IDs are not already open, they are opened automatically and shown in the tab bar.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image61.png" width="44" height="44">

</td><td>

**Select underlying objects** &ndash; This function currently provides the same result as **View in model**.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image92.png" width="44" height="44"> 

</td><td>

**Copy current report definition** &ndash; Copies the current report definition to the clipboard so that it can be saved in the report definition file.
For more details, see [**Report definitions**](#report-definitions) in [**Custom settings**](#custom-settings).

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image116.png" width="44" height="44">

</td><td>

**Customise Report Settings** &ndash; Opens the Report Editor and allows you to modify the current report definition.
For more details, see [**Report editor**](#report-editor).

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image56.png" width="44" height="44">

</td><td>

**Save Report** &ndash; Opens the save dialogue directly. You can save the report result as an `.xlsx` or `.csv` file for further use outside UDiTH. The button is available via the save options in the right panel.

</td></tr>
<tr><td>

<img src="./media/IntelliPID3_image62.png" width="44" height="44">

</td><td>

**Toggle quick filter** &ndash; Toggles the quick filter to narrow down report results.
When activated, another line is shown below the attribute names. Wildcards can be used:
<img src="./media/IntelliPID3_image63.png" width="400" height="50"> 
The line consists of a filter string field and two additional buttons:
<img src="./media/IntelliPID3_image64.png" width="40" height="30"> **Hide/show exclusive clauses** &ndash; Allows additional filters to be set for showing or hiding exactly defined values, and shows the current number of values set.
<img src="./media/IntelliPID3_image65.png" width="40" height="30"> **Clear filter** &ndash; Clears and resets all filters.

</td></tr> 
<tr><td>

<img src="./media/IntelliPID3_image66.png" width="44" height="44">

</td><td>

**Filter combine mode** &ndash; If several filters are set, they are combined with an **OR** operator by default, which means that only one filter condition must be true.
If you switch to **AND**, all filter conditions must be fulfilled for the item to be listed.

</td></tr>
</table>

#### Right-click Filter

To narrow down results further, you can also right-click, or long-tap, an attribute value in the result list. A pop-up appears that allows you to show or hide the selected values exclusively:

<img src="./media/IntelliPID3_image67.png" width="200" height="50">

All lines with the corresponding values are then either shown or hidden in the results list.

#### Custom Attribute Editing
If multiple lines are selected and a custom attribute with a CodeList is included in the report, it can be changed in bulk for all selected items:

<img src="./media/IntelliPID3_image68.png" width="200" height="120">|<img src="./media/IntelliPID3_image69.png" width="200" height="120">|<img src="./media/IntelliPID3_image70.png" width="200" height="120">

> *There is a limit of about 20 elements per row. The number of elements can be checked by adding a special attribute "count" in the report editor. For even more detailed reporting and advanced reporting features, use CAXperts [UniversalReporter](https://www.caxperts.com/universalreporter/).*

## Custom Settings

UDiTH uses a range of files that can be used to customise the content and options available in the software itself.
The following sections describe the different file types.

### Autostart Files

UDiTH automatically considers several default and settings files during start-up.
For this to work, the files must be named exactly as shown in the list below:

<table>
<tr><th>Location</th><th>Name</th><th>Function</th></tr>
<tr><td>

.\\Data\\

</td><td>

intelliPidLinks.xlsx

</td><td>

Linking file for external links and documents.

</td></tr>
<tr><td>

.\\Data\\

</td><td>

intelliPidAttributes.xlsx

</td><td>

Definition of custom attributes to be used in IntelliPID.

</td></tr>
<tr><td>

.\\Data\\

</td><td>

intelliPidAttributeData.xlsx

</td><td>

Custom attribute values for IntelliPID to be loaded on UDiTH startup.

</td></tr>
<tr><td>

.\\Data\\

</td><td>

intelliPidReportDefinition.xlsx

</td><td>

Definition of reports and their filters for IntelliPID.

</td></tr>
<tr><td>

.\\Data\\colorfiles\\

</td><td>

upvcolorindex.txt

</td><td>

Listing of the default display styles to be listed in the Display Style Selection in the UDiTH [**Load display style**](#display-styles) menu.

</td></tr>
<tr><td>

.\\Data\\

</td><td>

upvintellipidsketchitemindex.txt

</td><td>

Lists all catalogue items to be shown in IntelliPID Sketching.

</td></tr>
</table>

### Link Mapping Files

There are three ways to add links to your UDiTH model:

- During the building process through a link mapping file
- Automatically load a link mapping file on start-up (`Links.xlsx` in the `\Data\` folder of the model)
- Loading a link mapping file (see section **File panel** of the chapter [**Right panel**](#right-panel)).

All three methods use the same type of definition in the form of an Excel link mapping file:

<img src="./media/IntelliPID3_image167.png" width="600" height="80">

**Link** &ndash; The link can be a web link to a website or web-based database with the required link structure, an absolute path, or a path relative to the model's root directory, which is the directory containing the `Data` folder.

**Condition** &ndash; The condition can be any filter, including wildcards, that relates to an attribute in UDiTH.

**Name** &ndash; The name is the display name of the link. This is the text shown in the Attributes panel in UDiTH.

**Color** &ndash; The colour setting can be either a hexadecimal RGB colour definition preceded by **#**, or one of the Excel default colour names: **black, white, red, green, blue, yellow, magenta, and cyan**.

### Custom Attributes

Custom attributes are user defined attributes that can be altered before each launch of UDiTH. Their values can be changed at runtime (except calculation attributes). 
The Custom Attributes for IntelliPID are defined in the file **IntelliPidAttributes.xlsx**
This file can contain six worksheets (tabs):
- **Definition**
- **Codelist**
- **Filter**
- **Meta**
- **SketchDefinition**
- **LabelDefinition**

#### Worksheet "Definition"
In this worksheet, the custom attributes are defined:

<img src="./media/IntelliPID3_image168.png" width="600" height="100">

**Id** &ndash; The Id of the custom attribute can be any number. The only condition is that an Id must be unique throughout the complete custom attributes list. This Id is later used for value assignment.

**Name** &ndash; The displayed name of the attribute in UDiTH.

**SourceAttribute** &ndash; The attribute the custom attribute "attaches" itself to. E.g., SP_ID generates many entries since it attaches itself to any object with the attribute SP_ID. 
 This may influence the performance of UDiTH. It is highly recommended to use "SourceAttribute" as specific as possible.

**Filter** &ndash; Sets a filter to assign the custom attributes only to objects covered by the filter. A filter can be directly entered here (row 5), wildcards are valid. Instead, a predefined filter can be set (worksheet "Filter").

> *Please note that filters are applied sequentially. When the same filter is run several times in a definition, the according query will be executed several times. This can influence performance negatively. Instead, use predefined filters*.

**DataType** &ndash; The data type defines which properties an attribute will have. There are three basic types:

- **Calculation** &ndash; Defines a value that will be calculated on UDiTH launch. This is the only custom attribute that cannot be altered during runtime. For IntelliPID, only the calculation HasLink is available (DefaultValue "ThreeD" is mandatory) that gives back a Boolean if the object exists in 3D.

- **Freetext** &ndash; Defines a freely editable text attribute during runtime. Accepts all value strings.

- **Codelist** &ndash; Defines a codelist attribute. In UDiTH, these attributes are displayed as a combo box, enabling you to choose from a predefined codelist of values -- see worksheet "Codelist".

**DefaultValue** -- The value that will be assigned to the custom attribute automatically without any manual assignment being necessary. Depending on the DataType, there are different options available:

- **For Calculation** &ndash; Only DefaultValue "ThreeD" is available.

- **For Freetext** &ndash; can be left blank or contain any string.

- **For Codelist** &ndash; The default value should be a value listed in the codelist definition.

**Parameter1** &ndash; This parameter gives additional parameters, depending on the DataType of the attribute:

- **For Calculation** &ndash; Only parameter "HasLink" is available.

- **For Freetext** &ndash; not used

- **For Codelist** &ndash; The name of the codelist to use, see worksheet "Codelist"

**Id** &ndash; The Id of the custom attribute can be any number. The only condition is that an Id must be unique throughout the complete custom attributes list. This Id is later used for value assignment.

#### Worksheet "Codelist"
In this worksheet, the codelists for the custom attributes are defined:

<img src="./media/IntelliPID3_image169.png" width="600" height="190">

- **Name** &ndash; The name of the codelist. This is the Name used in Parameter1 on worksheet "Definition"

- **Value** &ndash; defines all values available for the codelist in column A (Name)

- **Color** &ndash; defines the color the respective value will be shown in in UDiTH. This also affects the color used in the legend. It can be either a hexadecimal RGB color definition preceded by a **\#** or one of the Excel default colors in clear name notation: **black, white, red, green, blue, yellow, magenta, and cyan**.

- **Line_Width_Factor** &ndash; modifies the line width of the P&ID objects by a factor. Factor 1 means no change to the line width.
- **MarkUp** &ndash; allows line highlighting of the P&ID objects. Transparency is controlled by the last two digits in the “Color” column and the width factor by the digit in the "Line_Width_Factor" column.

<img src="./media/IntelliPID3_image161.png" width="600" height="350">

#### Worksheet "Filter"
In this worksheet, often used filters for the custom attribute definition can be set:

Filters entered in the worksheet "Definition" are always executed separately, even if the contents of the filter strings are identical. This can slow down the loading of the custom attributes on UDiTH launch.

Instead, you can use the worksheet "Filter" to define a referenced filter. Filters defined here are run only once, no matter how often they are invoked on the worksheet "Definition". Whereas a direct filter runs every single time it is invoked, using the predefined filter reuses the result of the filter, thus increasing the performance of custom attributes loading.

<img src="./media/IntelliPID3_image170.png" width="600" height="60">

- **Name** &ndash; The unique name of the filter to be used as reference in the tab "Definition". This name can be freely assigned.

- **Filter** &ndash; The actual filter string. Wildcards are allowed. 

#### Worksheet "Meta"

In this worksheet, the parameters for the mailing of custom attribute changes can be defined:

<img src="./media/IntelliPID3_image171.png" width="600" height="80">

For IntelliPID, there are two keys available:

- **WorkcardMail_To** &ndash; the recipient or list of recipients for the list of custom attribute changes.

- **WorkcardMail_Subject** &ndash; the subject automatically added to the mail generated.

#### Worksheet "SketchDefinition"

In this worksheet, the changeable or read-only attributes can be defined for sketch items:

<img src="./media/IntelliPID3_image189.png" width="600" height="250">

- **Name** &ndash; The name of the attribute to be displayed on a sketch item.
- **Filter** &ndash; The name of the predefined filter from the worksheet "Filter". When a filter is set, the attributes will be applied to the sketch items defined in it.
- **DataType, DefaultValue, Parameter1** &ndash; Please refer to the description in the chapter [**Worksheet "Definition**](#worksheet-definition). 
- **ReadOnly** &ndash; True: attribute is read-only. False: attribute is changeable during runtime. 

#### Worksheet "LabelDefinition"

This worksheet allows admins to predefine different labels for sketch items. Labels can contain multiple entries, such as pipeline number. The order of the individual strings of one label corresponds to the order in the Excel rows:

<img src="./media/IntelliPID3_image190.png" width="600" height="350">

<img src="./media/IntelliPID3_image176.png" width=300 height=200>

- **Label_Name** &ndash; The displayed label name in the PIDSketch tab "Labels".
- **Entry_Type** &ndash; Attribute: The entry will be treated as an attribute with a changeable value. Codelist: The entry will be treated as a codelist attribute. Const: A fixed entry in the label string.
- **Entry_Name** &ndash; Attribute: The name for the displayed attribute. Codelist: The name for the codelist defined in the worksheet "Codelist". Const: Fixed text entry or line break operator "\
" to create a multi-line label.  
- **Default_Value** &ndash; Attribute, Codelist: The default value for the displayed attribute.
- **Font** &ndash; Specifies the system font to be used for the label.
- **Font_Size** &ndash; Specifies the font size to be used for the label.

### Report definitions

To define a report that will be listed in the report selection list, the file IntelliPidReportDefinition.xlsx is used. There are two ways to create a new report definition:

#### Definition by copy

Create a new report and in the **Report Results** screen use the **Copy current report definition** function. This will copy the current report definition to the clipboard.
Then paste the content of the clipboard to the next free row in the report definition file.

#### Manual definition
You can also create a report definition manually by editing the report definition file directly:

<img src="./media/IntelliPID3_image172.png" width="600" height="100">

There are the following fields used to define a custom report. The fields highlighted are optional fields. They can stay empty without any impact on the functionality:

- **Name** &ndash; The name of the codelist. This is the Name used in Parameter1 on worksheet "Definition".

- **Forbidden** (optional) &ndash; define actions you wish to block for the report: **C** (customize), **F** (filter), or **S** (save). 
 You can also combine these restrictions with comma or semicolon. Example: **C,F;S**

- **Description** (optional) &ndash; Additional description to explain the purpose of the report.

- **Sort Column** &ndash; Defines the column by which the report will be sorted. If no prefix is added, the report will be sorted in ascending order. 
 Optionally, you can add a prefix **ascending:** or **descending:** before the attribute.

- **Filter** (optional) &ndash; All attributes, including custom attributes as well as wildcards can be used for filtering.

- **Scope** (optional) &ndash; Defines the scope of P&IDs. Options are **All**, **All Open**, **CurrentDrawing**, and **Selected**.

- **Attributes** &ndash; Attributes to be included in the report. For every additional attribute, add an entry to the first free column to the right. 
The number of attributes to be reported on is not limited.

There are two additional special attributes for reporting: **Color** returns the current color of the object and **Count** gives you the number of objects contained in that object. For instance, a vessel with 4 nozzles will have a count of 5.

### Display styles
#### Color list file
UDiTH uses one central list file (upvcolorindex.txt) for the listing of available Display Styles. This file is located in the **colorfiles** folder of your model's **Data** folder.
It is automatically considered on launch. It contains the names of the color files to be show by default in the **Select display style** selection in the [**File panel**](#files-panel).

#### Color Files
You can create custom color files using Excel for the definition of colors, conditions and color groups.

<img src="./media/IntelliPID3_image173.png" width="600" height="60">

**Color** &ndash; defines colors to colorize the designated items. There are two ways to define a color:
- Use Excel colors in clear name notation: **black, white, red, green, blue, yellow, magenta, and cyan**.
- Use HTML (hexadecimal) color codes starting with \# followed by 6 digits for the basic colors plus two optional digits for opacity: **#RRGGBBOO**. 
Please note that the hexadecimal values range from 00 (decimal 0) to FF (decimal 255).

**Aspect** is an option for 3D coloring and is not used for IntelliPID display styles.

For **Condition** user can enter a filter for any attribute or custom attribute. Wildcards are supported.

**GroupName** is an optional value by which you can define separate color groups in one file. This enables you to define several different conditions without the need to define separate color files.
Conditions belonging to the same color group must have the exact same group name.

**Legend_text** &ndash; defines the descriptive text displayed next to the corresponding color in the legend.

**Legend_display** &ndash; controls whether the legend text is shown or hidden when the legend is displayed.

**Line_Width_Factor** &ndash; modifies the line width of the P&ID objects by a factor. Factor 1 means no change to the line width.

**MarkUp** &ndash; allows line highlighting of the P&ID objects. Transparency is controlled by the last two digits in the “Color” column and the width factor by the digit in the "Line_Width_Factor" column.

### Sketch item index file
To use custom sketching items, a sketch item index file has to be created and named **upvintellipidsketchitemindex.txt**. This file contains the paths to the according custom symbol files you wish to be included in your model.

A catalog item consists of a pair of files: an .svg file that defines the graphics of the symbol itself and a .png file that delivers the symbol preview to be shown in the catalog. A sample IntelliPID Sketch catalog can be downloaded [**here**](https://downloads.caxperts.com/#udith_sampledata). It includes process and signal line symbols, as well as off-page connector symbols.

The .svg custom symbols (Scalable Vector Graphics) can be created using either a variety of desktop software, web-based apps, and code editors or using SmartSketch and converting the .sym symbols using **AdapterForSpid**. 

IntelliPID recognizes the special flags stored in symbol.svg. These flags must be added manually to the SVG code. If converting SmartSketch symbols via AdapterForSpid, the flags can be entered directly into the configuration file.

#### Available parameters 

Svg general specification:

<table>
<tr><th>Flag</th><th>Function</th></tr>
<tr><td>

DrawingFactor="3780"

</td><td>

Fix entry for correct rendering of the symbol in IntelliPID.

</td></tr>
<tr><td>

Inkscape="1.4"

</td><td>

Fix entry for correct rendering of the symbol in IntelliPID.

</td></tr>
<tr><td>

SymbolOriginX="0.5"

</td><td>

Symbol's origin in x-axis. For symmetric symbol "0.5" means the mid-point of the symbols in x-axis.

</td></tr>
<tr><td>

SymbolOriginY="0.5"

</td><td>

Symbol's origin in y-axis. For symmetric symbol "0.5" means the mid-point of the symbols in y-axis.

</td></tr>
<tr><td>

ShowLine="1"

</td><td>

Symbol can be added to the routing without interrupting the line, e.g. insulation symbol.

</td></tr>
<tr><td>

KeepColor="1"

</td><td>

Symbol will keep its original color, independent of the PIDSketch Layer color.

</td></tr>
<tr><td>

Linestyle="1"

</td><td>

This parameter is used to create different routing patterns.

</td></tr>
<tr><td>

DoNotAllowAddObject="1"

</td><td>

This parameter works in conjunction with the "linestyle" flag. It determines whether a symbol can be added to the routing or not.

</td></tr>
<tr><td Colspan=2>

*Parameters for symbols with a leader line:*

</td></tr>
<tr><td>

LeaderX="0.5"

</td><td>

Specifies the relative x-coordinate for start point of leader line vector.

</td></tr>
<tr><td>

LeaderY="0.5" 

</td><td>

Specifies the relative y-coordinate for start point of leader line vector.

</td></tr>
<tr><td>

LeaderVectorX="0.15"

</td><td>

Specifies the relative x-coordinate for end point of initial leader line vector.

</td></tr>
<tr><td>

LeaderVectorY="-0.40"

</td><td>

Specifies the relative y-coordinate for end point of initial leader line vector.

</td></tr>
<tr><td>

LeaderSymbol="Path_to_Symbol"

</td><td>

It creates a combined symbol. It leads to a reference symbol that will be attached to the end of the leader line. For example, in the case of shutdown planning, you would add a mark to the valve together with the description.

</td></tr>
</table>

Parameters on svg path level:
<table>
<tr><th>Flag</th><th>Function</th></tr>
<tr><td>

type ="start"

</td><td>

symbol for the start terminator of the line.

</td></tr>
<tr><td>

type ="end"

</td><td>

symbol for the end terminator of the line.

</td></tr>
<tr><td>

type ="auxiliary"

</td><td>

flag for an auxiliary line (e.g. heat tracing).

</td></tr>
</table>

Parameters on svg text level:
<table>
<tr><th>Flag</th><th>Function</th></tr>
<tr><td>

attribute="Your Text"

</td><td>

It flags text that should be used as an attribute within a custom IntelliPID symbol and made visible on the symbol.

</td></tr>
<tr><td>

attribute="Opc_opctag"

</td><td>

It adds a special tag that can connect existing OPCs and sketch OPCs and make it visible on the symbol. This tag is added automatically when the symbol contains "*opc*" in the name.

</td></tr>
</table>

#### Adding object to the catalogue
You can add any .svg file and a corresponding .png file (optional) to your catalogue. UDiTH will include it on model launch if the object is valid and properly listed with name and path in the file Sketch Item index file. You can use absolute paths, relative paths and include:

- **Absolute path** &ndash; C:\\temp\\Pumps\\pump2.svg

- **Relative Path** &ndash; IntelliPidCatalogue\\Valves\\valve1.svg (relative to the location of your UDiTH model folder)

- **Include** &ndash; #include D:\\yourindex.txt - Include can be used to manage objects for the whole company in as many files as needed, rather than adding single objects to single models. 
In this example yourindex.txt stands for a separate Sketch Item index file.

## Deep linking in IntelliPID

> ℹ️ If you use UDiTH Portal, create the link as a [model link](/UDiTH%20Portal/Linking) instead. The manual method described below applies when no Portal is available.

You can include deep links in all types of documents that support links, i.e. Word, websites, email ... to open UDiTH and transfer a combination of queries and commands.

Deep linking uses the URL handler upvapi://

**Structure:**
upvapi://\<MODEL-URL\>?\<QUERY\>&\<COMMANDS\>

**Example:**
upvapi://http://demo.universalplantviewer.com/demoPlant/11/0/?Equipment%20ITEMTAG=D-240&CMD!Target=IntelliPid&CMD!OpenIntellipidDrawings&CMD!Select&CMD!Fit/

This will look for the Equipment_ITEMTAG D-240, open the according drawing in IntelliPID, select the Item, highlight it and fit it to the screen.

> *There are no error messages for broken links, for example due to a typo. Please use %20 for including space characters*.

**Wildcards:**

- \* = any character

- \_ = any single character

#### Condition operators
By default query conditions are combined with **AND**:

Equipment%20ITEMTAG=K-001&Nozzle Name=N5

**OR** is possible with the combine command:

Equipment%20ITEMTAG=K-001&Equipment%20ITEMTAG=D-100&CMD!Combine=OR

---

# UDiTH App — Linking and API

# Linking and API

> ℹ️ **If you use UDiTH Portal, do not build links manually.**
>
> The recommended way to create a shareable link or a deep link to a model is a Portal model link: `https://<portal-url>/modellink/<alias-or-identifier>`. The Portal resolves the alias, selects the version, and applies authentication and permissions for you. See [Linking](/UDiTH%20Portal/Linking).
>
> The manual `upvapi://` method described on this page applies when UDiTH Portal is **not** in use.

## Link Files

-   To link an object to an external source, use a **link file**.
-   For linking from an external source to an object in UDiTH, use **deep linking**.

**Deep linking API: Link from outside \> UDiTH \> Link to outside**

UDiTH uses link files to store link information. Link files are loaded automatically when you open UDiTH, but you can also load them manually at any time via **File management > Load a link file** (<img src="./media/ButtonLoadLinkedFile.png" width="21" height="22">).

## Create a Link File

Link files are created in Excel. The file should contain the following columns:

<table>
<tr><th>Condition</th><th>Link</th><th>Name</th></tr>
<tr><td>

Task=Piping

</td><td>

https://www.caxperts.com/

</td><td>

Homepage

</td></tr>
<tr><td>

Task=Structure

</td><td>

https://en.wikipedia.org/wiki/Structure

</td><td>

Wikipedia

</td></tr>
</table>

-   **Condition** can be any attribute. `*` can be used as a wildcard to select every object.

For example: `Task=*`

-   **Link** can be a URL or a local path. Absolute and relative paths are supported using the backslash `\`.

Example of a local path: `data\docs\Valve.pdf`

-   **Name** is the text displayed in UDiTH for the link.

## Links in Isometrics

UDiTH automatically links objects in 3D models with their corresponding symbols in isometric and P&ID drawings. As a result, you can access drawings from both the **2D and P&ID** level of your project tree and the link list.

You can select an object in the 3D model by clicking on a symbol in a drawing. The symbol is highlighted when you hover your mouse pointer over it:

<img src="./media/image187.png" width="300" height="166"><img src="./media/image188.png" width="207" height="171">

Because drawings are rendered in PDF format, you can zoom in without any loss of quality.

You can also generate a double-sided ISO PDF and preview the 3D objects to be included.

> Links to objects in the 3D model are not retained.

## Deep Linking without UDiTH Portal

> If you use UDiTH Portal, use a [model link](/UDiTH%20Portal/Linking) instead. The Portal generates and manages these URLs for you, and the link stays valid when a new model version is published. The manual approach described below applies when no Portal is available.

You can use deep links in documents such as Word files, websites, and emails to open UDiTH and execute specific queries and commands.

Deep linking uses the URL handler `upvapi://`.

**Structure:**

`upvapi://<MODEL-URL>?<QUERY>&<COMMANDS>`

**Example:**

`upvapi://http://demo.universalplantviewer.com/demoPlant/4/0?Name=K-001&NozzleName=N3&CMD!Select&CMD!Fit&CMD!Highlight`

**Wildcards:**

Use `_` for any single character and `*` for any number of characters.

**Example:**

`upvapi://http://demo.universalplantviewer.com/demoPlant/4/0?Name=K-001&NozzleName=N3&CMD!Select&CMD!Fit&CMD!Highlight`

This looks for Nozzle 3 on object `K-001`, selects it, fits it to the screen, and greys out all other objects.

### `UDiTHSelected`

By adding `UDiTHSelected=true` or `UDiTHSelected=false`, you can limit the query to items that are currently selected or not selected in UDiTH.

**Example:**

`upvapi://http://demo.universalplanviewer.com/demoPlant/5/0?Name=D-100&UDiTHSelected=true&CMD!Select&CMD!Fit&CMD!FocusViewer&CMD!Combine=Or`

In this example, `Combine=Or` adds all items with `Name=D-100` to the current selection.

> Broken links, for example those caused by typing mistakes, do not produce an error message.

### Building Deep Links with Excel

In Excel, you can use the `CONCATENATE` and `HYPERLINK` functions to build a list of links easily.

Enter your model URL or path together with columns for the attributes, values, and commands you want to include:

> Paths may not work if they contain special characters. Use a URL encoder to ensure that the paths are correct.

<table>
<tr><th></th><th>A</th><th>B</th><th>C</th></tr>
<tr><td>

1

</td><td>

Model URL

</td><td>

...

</td><td>

-

</td></tr>
<tr><td>

2

</td><td>

Attribute

</td><td>

Value

</td><td>

Commands

</td></tr>
<tr><td>

3

</td><td>

Name

</td><td>

D-100

</td><td>

CMD!Fit

</td></tr>
<tr><td>

4

</td><td>

Task

</td><td>

Piping

</td><td>

CMD!SetVisibility=false

</td></tr>
<tr><td>

5

</td><td>

Pipeline

</td><td>

S-1672

</td><td>

CMD!Select&CMD!IntelliClip=1

</td></tr>
<tr><td>

6

</td><td>

Task

</td><td>

\*

</td><td>

CMD!SetCameraView=3037!2476!72!180!180!10

</td></tr>
<tr><td>

7

</td><td>

...

</td><td>

</td><td>

</td></tr>
</table>

Then add a function using the following structure:

`=HYPERLINK(CONCATENATE("upvapi://";$B$1;"?";A4;"=";B4;"&";C4))`

**Table 1: Commands**

> ℹ️ These commands are also available in a Portal model link, through the `commands` query parameter. See [Linking](/UDiTH%20Portal/Linking).

<table>
<tr><th>Command</th><th>Description</th></tr>
<tr><td>

ClearClipping

</td><td>

Disables clipping.

</td></tr>
<tr><td>

ClearColor

</td><td>

Restores the original object colours.

</td></tr>
<tr><td>

ClearCustomAttributes

</td><td>

Resets all changes made to custom attributes during the current session.

</td></tr>
<tr><td>

ClearHighlight

</td><td>

Disables highlighting.

</td></tr>
<tr><td>

ClearLinks

</td><td>

Removes links loaded from link files.

</td></tr>
<tr><td>

ClearSelection

</td><td>

Clears the current selection.

</td></tr>
<tr><td>

Color

</td><td>

`Color=[HTML colour code]` using the format `#RRGGBB` or `#RRGGBBAA`.
**Example:** `CMD!Color=#FF0000`

</td></tr>
<tr><td>

Combine

</td><td>

`Combine=[OR/AND]` uses `OR` between query conditions instead of `AND`, which is the default.

</td></tr>
<tr><td>

ExportPackageFile

</td><td>

Exports package files to the specified path.

</td></tr>
<tr><td>

Fit

</td><td>

Optional parameter: `Centred`.
`true` (default): fits to the centre of the box.
`false`: fits to the screen borders of the current view.
**Example:** `CMD!Fit=true`

</td></tr>
<tr><td>

FocusViewer

</td><td>

Brings the UDiTH window to the foreground and gives it the input focus. This command only has an effect on a local model.

</td></tr>
<tr><td>

Highlight

</td><td>

Highlights the specified object.
**Example:** `Name=D-100&CMD!Highlight`

</td></tr>
<tr><td>

IntelliClip

</td><td>

`IntelliClip=[distance in metres]`
Values `> 0` display only the object. `0` also displays objects touching the selected object. Decimal values are supported.

</td></tr>
<tr><td>

LoadColorFile

</td><td>

`LoadColorFile=[online or offline location]`
Local:
`CMD!LoadColorFile=C:\colors.xlsx`

Via HTTP:
`CMD!LoadColorFile=http://example.com/colors.xlsx`

Via UNC:
`CMD!LoadColorFile=file://////server/share/colors.xlsx`

Note: for UNC paths, it is important to use six slashes (`//////`) after `file`.

**Examples:**

`CMD!LoadColorFile=c:\colors.xlsx`
`CMD!LoadColorFile=http://demo.universalplantviewer.com/demoPlant/4/0/colors.xlsx`

</td></tr>
<tr><td>

LoadConfigFile

</td><td>

Loads a configuration file.
Parameters:
- configuration file name
- combine mode (optional)

By default, existing settings are cleared. `Merge` keeps existing views and merges them, overriding existing values with those from the configuration file.

**Example:** `C:/config.upv!Merge`

</td></tr>
<tr><td>

LoadLinkFile

</td><td>

`LoadLinkFile=[online or offline location]`

Local: `CMD!LoadLinkFile=C:\links.xlsx`

Via HTTP: `CMD!LoadLinkFile=http://example.com/links.xlsx`

Via UNC: `CMD!LoadLinkFile=file://////server/share/links.xlsx`

Note: for UNC paths, it is important to use six slashes (`//////`) after `file`.

</td></tr>
<tr><td>

LoadPackageFile

</td><td>

Loads package files from the specified path.

Parameters:
Parameters:
- URL
- `Replace` (optional). If set to `true`, current packages are replaced instead of being added to the existing ones.

**Example:**
`CMD!LoadPackageFile=c:\package.upvf!true`

</td></tr>
<tr><td>

LoadSketch

</td><td>

Loads a sketch from the specified path.
A second parameter can be used to rename the sketch.
**Example:**
`CMD!LoadSketch=C:\Sketch\SketchFile.upvsk!NewName`

</td></tr>
<tr><td>

ResetView

</td><td>

Resets the camera mode, camera position, rotation, and states such as highlighting to the state used when the model was loaded.

</td></tr>
<tr><td>

SearchFallback

</td><td>

This command automatically adds the search string to the search box. If the exact search returns no results, a fuzzy search is started automatically.

**Example:**
`upvapi://http://demo.universalplantviewer.com/demoPlant/4/0?Name=E240&NozzleName=N8&CMD!Select&CMD!Fit&CMD!Searchfallback`

This starts a search and finds Nozzle N8 on equipment E-240.

</td></tr>
<tr><td>

Select

</td><td>

Selects the specified object.
**Example:**
`Name=D-100&CMD!Select`

</td></tr>
<tr><td>

SetCameraView

</td><td>

`SetCameraView=[X!Y!Z!rotationX!rotationY!rotationZ]` using Euler angles.

**Example:**
`CMD!SetCameraView=3037!2476!72!180!180!90` sets the camera to coordinates `3037` (X), `2476` (Y), and `72` (Z = height), facing parallel to the ground and rotated `90°` to the left.

</td></tr>
<tr><td>

SetCameraViewLookAtTarget

</td><td>

`SetCameraViewLookAtTarget=[X!Y!Z!TargetX!TargetY!TargetZ]`
Moves the camera to the specified location and points it towards the target location.

</td></tr>
<tr><td>

SetClippingPlane

</td><td>

Sets the visible camera view distance.
The first parameter is the minimum distance for visible objects, and the second is the maximum distance.
**Example:** `CMD!SetClippingPlane=20!30`

</td></tr>
<tr><td>

SetStorageVariable

</td><td>

Sets a storage variable.
Parameters, in this order:
- the key to set
- the value
- an optional persist flag, `true` or `false`. The default is `false`.

Use this command with the LocalStorage Universal API system to send a message to a running AppControl.

**Example:** `CMD!SetStorageVariable=MyKey!MyValue`

</td></tr>
<tr><td>

SetTreeConfiguration

</td><td>

`SetTreeConfiguration=[discipline!discipline...]`
**Example:**
`CMD!SetTreeConfiguration=Task!SystemPath`

</td></tr>
<tr><td>

SetVisibility

</td><td>

`SetVisibility=[True/False]`

</td></tr>
<tr><td>

SetVisibleAspects

</td><td>

`SetVisibleAspects=[aspect!aspect]`
**Example:**
`CMD!SetVisibleAspects=Simple Physical!Insulation` shows both insulation and simple physical.

</td></tr>
<tr><td>

ShowOnly

</td><td>

Sets all query objects to visible and hides all other objects.

</td></tr>
<tr><td>

ShowMessage

</td><td>

Shows a message and title in a message box.
**Example:** `CMD!ShowMessage=Your Message!Your Title`

</td></tr>
<tr><td>

TakeAndSaveScreenshot

</td><td>

Takes a screenshot using the following parameters, in this order:

- path where the image is to be stored
- width in pixels
- height in pixels
- field of view in degrees (`> 160` may produce incorrect images)
- format, either `.png` or `.jpg`
- hide UI, `true` or `false`
- enable anti-aliasing, `true` or `false`
- enable transparent background, `true` or `false`
- model in best quality, `true` or `false`

**Example:** `CMD!TakeAndSaveScreenshot=C:\Test\image.jpg!2000!1500!120!png!true!true!true!false`

</td></tr>
<tr><td>

VolumeClip

</td><td>

`VolumeClip=[distance in metres]`
Values `< 0` are treated as `0`. Decimal values are supported.

</td></tr>
<tr><td>

VolumeClipByCoordinates

</td><td>

Runs the volume clip algorithm using a bounding box defined by the following parameters:
- X min
- Y min
- Z min
- X max
- Y max
- Z max

**Example:** `CMD!VolumeClipByCoordinates=-10.0!-10.0!-10.0!20.0!20.0!20.0`

</td></tr>
</table>

**Wildcards:**

-   **\*** = any character

-   **\_** = any single character

### Condition Operators

By default, query conditions are combined using **AND**:

For example:
```
Name=K-001&NozzleName=N5
```

However, you can use **OR** with the combine command:

For example:
```
Name=K-001&Name=D-100&CMD!Combine=OR
```

## TCP API Module
The TCP API Module has been deprecated and replaced by the Universal API which is available via AppControls, Websocket and BBV. Existing APIs continue to work, but no new APIs are added to the legacy API.

For more details, please refer to the AppControl and Websocket Feature sections of the documentation documentation or visit the developer documentation under https://github.com/caxperts/AppControls.

---

# UDiTH App — Appendix - Custom settings files

# Appendix - Custom Settings Files

UDiTH uses a range of files that can be used to customise both the content and the options available in the software.
This appendix provides an overview of the different file types.

## Configuration File

By editing the configuration file, you can hide buttons in the menu on the left, the tree bar at the top of the screen, and the menu on the right.
Only the tab bar at the bottom cannot be hidden, so that PDFs and other internal documents can still be viewed and closed.

The configuration is stored in `defaultConfig.upv` in the same folder as `UDiTH.exe` and can be edited using a text editor.

### MenuItemVisibilitySettings

Table 8 lists the available menu item visibility configuration.

<table>
<tr><th>Icon</th><th>Key</th><th>Description</th></tr>

<tr><td>

<img src="./media/ButtonSettings.png" width="44" height="44">

</td><td>

SettingsButton

</td><td>

Hide the **Settings** button.

</td></tr>
<tr><td>

<img src="./media/ButtonSettings.png" width="44" height="44"><img src="./media/ButtonProjectView.png" width="44" height="44">

</td><td>

TreeToggleButton

</td><td>

Hide the **nTreeToggleButton** button.

</td></tr>
<tr><td>

<img src="./media/ButtonSettings.png" width="44" height="44"><img src="./media/ButtonTreeConfig.png" width="44" height="44">

</td><td>

ProjectTreeConfigurationButton

</td><td>

Hide the **Configure the project tree** button.

</td></tr>
<tr><td>

<img src="./media/ButtonAdd.png" width="44" height="44">

</td><td>

FilesTreeAddButton

</td><td>

Hide **Add File** button.

</td></tr>

</table>

> Keep in mind that certain elements can influence others.

> MenuItemVisibilitySettings is available from UDiTH version 2026.4.0 onwards. 

**Example:** The following configuration hides the all the buttons in mentioned above:

```json
{
  "CommonSettings": [
    {
      "MenuItemVisibilitySettings": {
        "SettingsButton": false,
        "TreeToggleButton": false,
        "ProjectTreeConfigurationButton": false,
        "FilesTreeAddButton": false
      }
    }
  ]
}
```
---

The configuration is divided into four blocks:

-   **CurrentViewSettings**: Defines the default view when the UDiTH model is opened for the first time or reset.

-   **Views**: Stores all saved views.

-   **CommonSettings/UserSettings**: The default UDiTH settings can be distributed between these two blocks as required. Use `CommonSettings` for settings that should apply to all users, and `UserSettings` for settings that are specific to individual users.

Blocks from multiple configurations are merged. Common settings are not saved by default.

### Filters

You can define filters for setting blocks in both `CommonSettings` and `UserSettings`.

UDiTH always uses the first matching block for each unique feature. All following matches are ignored.

A `Filters` block can contain multiple clauses. These are linked with the `OR` operator.

**Example:**

```json
"CommonSettings": [
  {
    "Filters": [
      { "DeviceType": "IOS" },
      { "DeviceType": "Android" }
    ],
    "LodTrianglesLimit": 50000000
  },
  {
    "LodTrianglesLimit": 50000001
  }
]
```

In this example, all users with an Android or iOS device have a triangles limit of `50000000`.

All other users have a triangles limit of `50000001`.

### Possible Filters

`UserDomainName = "Caxperts01"`

`DisplaySize = ...` options: `Smartphone` (`< 9 inch`), `Tablet` (`9-14 inch`), `Desktop` (`14-28 inch`), `Large` (`28-40 inch`), `VeryLarge` (`> 40 inch`)

`DeviceType = ...` options: `Windows`, `IOS`, `Android`, `UniversalWindowsPlatform`

`DeviceName = ...`

`UserName = ...`

### Recap: Elements that can be predefined / added

Predefining certain elements can speed up work in UDiTH, especially for routine tasks.

The relevant chapters describe how to predefine:

-   Views

-   Volumes

-   Reports

You can also add:

-   Colour files

-   Link files

-   Object lists for creating double-sided ISOs

## Autostart Files

UDiTH automatically considers a number of default and settings files during start-up. For this to work, the files must be named exactly as shown below:

| Location | Name | Function |
| --- | --- | --- |
| `.\Data\` | `default.upvf` | Default UPVF file to load at startup. |
| `.\Data\` | `links.xlsx` | Linking file for external links and documents. |
| `.\Data\` | `intelliPidLinks.xlsx` | Linking file for external links and documents in IntelliPID. |
| `.\Data\` | `attributes.xlsx` | Definition of custom attributes to be used. |
| `.\Data\` | `intelliPidAttributes.xlsx` | Definition of custom attributes to be used in IntelliPID. |
| `.\Data\` | `attributeData.xlsx` | Custom attribute values to be loaded on UDiTH start-up. |
| `.\Data\` | `intelliPidAttributeData.xlsx` | Custom attribute values for IntelliPID to be loaded on UDiTH start-up. |
| `.\Data\` | `ReportDefinition.xlsx` | Definition of reports and their filters. |
| `.\Data\` | `intelliPidReportDefinition.xlsx` | Definition of reports and their filters for IntelliPID. |
| `.\Data\colorfiles\` | `upvcolorindex.txt` | Listing of the default display styles shown in the UDiTH **Load display style** menu. |
| `.\Data\` | `upvsketchitemindex.txt` | Lists all catalogue items to be shown in sketching. |
| `.\Data\` | `upvintellipidsketchitemindex.txt` | Lists all catalogue items to be shown in IntelliPID sketching. |
| `.\Data\` | `appcontrols.json` | Provides configuration to AppControl usague restrictions |

## Link Mapping Files

There are three ways to add links to your UDiTH model:

-   During the building process through a link mapping file

-   Automatically loading a link mapping file on start-up (`links.xlsx` in the `\Data\` folder of the model)

-   Loading a link mapping file manually, see **File menu** in the chapter **Left side menu**.

All three methods use the same type of definition in the form of an Excel link mapping file:

<img src="./media/IntelliPID_image127.png" width="705" height="94">

**Link** - The link can be a web link to a website or web-based database with the required link structure, an absolute path, or a path relative to the model's root directory, which is the directory that contains the `Data` folder.

**Condition** - The condition can be any filter, including wildcards, that relates to an attribute in UDiTH.

**Name** - The name is the display name of the link. This is the text shown in the Attributes panel in UDiTH.

**Colour** - The colour setting can be either a hexadecimal RGB colour definition preceded by a **\#** or one of the Excel default colours in
clear name notation: **black, white, red, green, blue, yellow, magenta, and cyan**.

## Custom Attributes

Custom attributes are user-defined attributes that can be changed before each launch of UDiTH. Their values can also be changed at runtime, except for calculation attributes. The custom attributes for IntelliPID are defined in `intelliPidAttributes.xlsx`. This file can contain four worksheets (tabs):

-   **Definition**

-   **Codelist**

-   **Filter**

-   **Meta**

### Worksheet "Definition"

<img src="./media/IntelliPID_image128.png" width="768" height="143">

This worksheet defines the custom attributes.

**Id** -- The ID of the custom attribute can be any number. The only requirement is that each ID must be unique throughout the full list of custom attributes. This ID is later used for value assignment.

**Name** -- The name under which the attribute is displayed in UDiTH.

**SourceAttribute** -- The attribute to which the custom attribute attaches itself. For example, `SP_ID` can generate many entries because it attaches itself to every object with the attribute `SP_ID`. This can affect UDiTH performance.
It is strongly recommended to define `SourceAttribute` as specifically as possible.

**Filter** -- Sets a filter so that custom attributes are assigned only to objects covered by that filter. A filter can be entered directly here, and wildcards are supported. Alternatively, a predefined filter can be used from the `Filter` worksheet.

> Filters are applied sequentially. Consider using predefined filters instead. When the same filter is used several times in a definition, the corresponding query is executed several times, which can reduce performance.

**DataType** -- The data type defines which properties an attribute has. There are three basic types:

-   **Calculation** -- Defines a value that is calculated when UDiTH starts. This is the only type of custom attribute that cannot be changed at runtime. In IntelliPID, only the `HasLink` calculation is available. `DefaultValue="ThreeD"` is mandatory, and the result is a Boolean value that indicates whether the object exists in 3D.

-   **Freetext** -- Defines a freely editable text attribute that can be changed at runtime. All value strings are accepted.

-   **Codelist** -- Defines a codelist attribute. In UDiTH, these attributes are displayed as a combo box, allowing you to choose from a predefined list of values. See the `Codelist` worksheet.

**DefaultValue** -- The value that is assigned automatically to the custom attribute without any manual input.
Depending on the `DataType`, different options are available:

-   **For Calculation** -- Only `DefaultValue="ThreeD"` is available.

-   **For Freetext** -- Can be left blank or contain any string.

-   **For Codelist** -- The default value should be one of the values listed in the codelist definition.

**Parameter1** -- This field provides additional parameters depending on the `DataType` of the attribute:

-   **For Calculation** -- Only the parameter `HasLink` is available.

-   **For Freetext** -- Not used.

-   **For Codelist** -- The name of the codelist to use. See the `Codelist` worksheet.

The `Id` field must remain unique throughout the complete custom attributes list.

### Worksheet "Codelist"

<img src="./media/IntelliPID_image129.png" width="680" height="237">

This worksheet defines the codelists for custom attributes.

**Name** -- The name of the codelist. This is the same name used in `Parameter1` on the `Definition` worksheet.

**Value** -- Defines all values available for the codelist under the corresponding name.

**Colour** -- Defines the colour in which the respective value is shown in UDiTH. This also affects the colour used in the legend. It can be either a hexadecimal RGB colour definition preceded by `#`, or one of the Excel default colour names: **black, white, red, green, blue, yellow, magenta, and cyan**.

### Worksheet "Filter"

This worksheet is used to define frequently used filters for custom attributes.

Filters entered directly in the `Definition` worksheet are always executed separately, even if the filter strings are identical.
This can slow down the loading of custom attributes when UDiTH starts.

Instead, you can use the `Filter` worksheet to define a referenced filter. Filters defined here are executed only once, no matter how often they are referenced from the `Definition` worksheet. A direct filter runs every time it is used, whereas a predefined filter reuses the result and therefore improves performance when loading custom attributes.

<img src="./media/IntelliPID_image130.png" width="635" height="65">

**Name** -- The unique name of the filter to be used as a reference. This name can be chosen freely.

**Filter** -- The actual filter string, as described in the `Definition` worksheet. Wildcards are supported.

### Worksheet "Meta"

This worksheet defines the parameters used for emailing custom attribute changes.

<img src="./media/IntelliPID_image131.png" width="619" height="83">

For IntelliPID, two keys are available:

-   **WorkcardMail_To** -- The recipient or list of recipients for the list of custom attribute changes.

-   **WorkcardMail_Subject** -- The subject that is added automatically to the generated email.

## Report Definitions

To define a report that appears in the report selection list, use the file `intelliPidReportDefinition.xlsx`. There are two ways to create a new report definition:

### Definition by Copy

Create a new report and use **Copy current report definition** in the **Report Results** screen. This copies the current report definition to the clipboard. You can then paste the clipboard contents into the next free row of the report definition file.

### Manual Definition

You can also create a report definition manually by editing the report definition file directly:

<img src="./media/IntelliPID_image132.png" width="736" height="113">

The following fields are used to define a custom report. The highlighted fields are optional and can be left empty without affecting functionality:

**Name** -- The name of the report.

**Forbidden** (optional) -- define actions you wish to block for the report: **C** (customise), **F** (filter), or **S** (save).
You can also combine these restrictions with comma or semicolon.
Example: **C,F;S**

**Description** (optional) -- An additional description explaining the purpose of the report.

**Sort Column** -- Defines the column by which the report is sorted. If no prefix is added, the report is sorted in ascending order. Optionally, you can add the prefix `ascending:` or `descending:` before the attribute.

**Filter** (optional) -- All attributes, including custom attributes as well as wildcards can be used for filtering.

**Scope** (optional) -- Defines the scope of the P&IDs. Options are **All**, **All Open**, **CurrentDrawing**, and **Selected**.

**Attributes** -- The attributes to be included in the report. For each additional attribute, add an entry in the next free column to the right. The number of attributes is not limited.

There are two additional special attributes for reporting: **Colour** returns the current colour of the object, and **Count** returns the number of objects contained within that object. For example, a vessel with four nozzles has a count of five.

## Display Styles

### Colour List File

UDiTH uses one central list file, `upvcolorindex.txt`, to define the available display styles. This file is located in the **colourfiles** folder within your model's **Data** folder. It is loaded automatically when UDiTH starts. It contains the names of the colour files that are shown by default in the **Set display style** selection in the **File menu**.

### Colour Files

You can create custom colour files in Excel to define colours, conditions, and colour groups.

<img src="./media/IntelliPID_image133.png" width="487" height="143">

**Colour** -- Defines the colours used to colourise the designated items. There are two ways to define a colour:

-   Use Excel colours in clear name notation: **black, white, red, green, blue, yellow, magenta, and cyan**.

-   Use HTML (hexadecimal) colour codes starting with \# followed by 6 digits for the basic colours plus two optional digits for opacity:
    **\#RRGGBBOO**. Please note that the hexadecimal values range from 00 (decimal 0) to FF (decimal 255).

For **Condition**, you can enter a filter for any attribute or custom attribute. Wildcards are supported.

**Aspect** is an option for 3D colouring and is not used for IntelliPID display styles.

**GroupName** is an optional value that allows you to define separate colour groups within one file. This lets you define several different conditions without needing separate colour files. Conditions that belong to the same colour group must use exactly the same group name.

## Sketch Item Index File

To use custom sketching items, you must create a Sketch Item index file named `upvintellipidsketchitemindex.txt`. This file contains the paths to the Sketch Item files that you want to include in your model.

A catalogue item consists of a pair of files: an `.svg` file, which defines the graphics of the symbol itself, and a `.jpg` or `.png` file, which provides the preview shown in the catalogue.

### Adding Objects to the Catalogue

You can add any `.svg` file, together with an optional corresponding `.jpg` or `.png` file, to your catalogue. UDiTH loads it when the model starts if the object is valid and correctly listed, with name and path, in the Sketch Item index file. You can use absolute paths, relative paths, and includes:

**Absolute path** - `C:\temp\Pumps\pump2.svg`

**Relative path** -- `IntelliPidCatalogue\Valves\valve1.svg` (relative to the location of your UDiTH model folder)

**Include** - `#include D:\yourindex.txt` - Include can be used to manage objects for the whole company across as many files as required, rather than adding individual objects to individual models. In this example, `yourindex.txt` refers to a separate Sketch Item index file.

## Deep Linking in IntelliPID

You can include deep links in any document type that supports links, such as Word files, websites, or emails, to open UDiTH and transfer a combination of queries and commands.

Deep linking uses the URL handler `upvapi://`.

**Structure:**

`upvapi://<MODEL-URL>?<QUERY>&<COMMANDS>`

**Example:**

`upvapi://http://demo.universalplantviewer.com/demoPlant/6/0?Equipment%20ITEMTAG=D-240&CMD!Target=IntelliPid&CMD!OpenIntellipidDrawings&CMD!Select&CMD!Fit`

This looks for `Equipment_ITEMTAG=D-240`, opens the corresponding drawing in IntelliPID, selects the item, highlights it, and fits it to the screen.

> Broken links, for example those caused by typing mistakes, do not produce an error message. Use `%20` for spaces.

**Wildcards:**

-   `*` = any character

-   `_` = any single character

### Condition Operators

By default, query conditions are combined with **AND**:

`Equipment%20ITEMTAG=K-001&Nozzle Name=N5`

You can use **OR** with the combine command:

`Equipment%20ITEMTAG=K-001&Equipment%20ITEMTAG=D-100&CMD!Combine=OR`

## Drawing Templates

Creating a drawing requires a drawing template. The available drawing templates are defined in the text file `drawingtemplates.json`, which is located in the UDiTH model data folder: `Data\drawingtemplates`.

Each drawing template contains all necessary information, including the name, the drawing border file (`.svg`), and the parameters for one or more drawing views.

The drawing border files can be located in any folder within the `drawingtemplates` folder.

Each drawing template requires the parameters shown in the table below.

| Parameter | Description |
| --- | --- |
| `Name` | The template name as shown in UDiTH. |
| `BorderFile` | The `.svg` file, including its relative path to the `drawingtemplates` folder, for example `en/EN_Metric_A1_Border_Wide.svg`. |
| `DrawingArea` | Contains the parameters for each drawing view. |
| `Top` | The upper border of the drawing view in `[mm]`. |
| `Left` | The left border of the drawing view in `[mm]`. |
| `Width` | The maximum width of the drawing view in `[mm]`. |
| `Height` | The maximum height of the drawing view in `[mm]`. |
| `ViewDirection` | Defines the view direction for the drawing view: `FromNorth = 0`, `FromEast = 1`, `FromSouth = 2`, `FromWest = 3`, `FromTop = 4`, `FromBottom = 5`, `FromTopSouthWest = 6`, `FromTopSouthEast = 7`, `FromTopNorthWest = 8`, `FromTopNorthEast = 9`, `FromBottomSouthWest = 10`, `FromBottomSouthEast = 11`, `FromBottomNorthWest = 12`, `FromBottomNorthEast = 13`. |
| `PrintScale` | A predefined fixed scale. For example, `100` defines a scale of `1:100`; `0.0` defines automatic scaling. |
| `Fit` | If `true`, the maximum possible scale, for example `1:178`, is used. If `false` or missing, the maximum possible standard scale, for example `1:200`, is used. |
| `UseCommonScale` | `true` or `false`. `true` means the drawing area uses the lowest possible common scale for all drawing areas marked `UseCommonScale = true`. `false` means the drawing area uses the lowest possible scale for that individual drawing area. The calculated scale is shown in the drawing if the `.svg` file contains a text field marked as `###SCALE###`. |

Examples of two simple templates:

```json
[

{

\"Name\": \"EN, Metric, A1 Wide, Isometric View\",

\"BorderFile\": \"en/EN_Metric_A1_Border_Wide.svg\",

\"DrawingAreas\": \[

{

\"Top\": 12.0,

\"Left\": 22.0,

\"Width\": 806.0,

\"Height\": 515.0,

\"ViewDirection\": 6,

\"PrintScale\": 0.0,

\"Fit\": false,

\"UseCommonScale\": true

}

\]

},

{

\"Name\": \"EN, Metric, A4 Wide, Top View\",

\"BorderFile\": \"en/EN_Metric_A1_Border_Wide.svg\",

\"DrawingAreas\": \[

{

\"Top\": 12.0,

\"Left\": 22.0,

\"Width\": 806.0,

\"Height\": 515.0,

\"ViewDirection\": 4,

\"PrintScale\": 0.0,

\"Fit\": false,

\"UseCommonScale\": true

}

\]

}

]
```

Example of multiple drawing views in a single template, for example for a standard orthographic projection:

```json
[

{

\"Name\": \"EN, Metric, A3 Wide, Standard Views\",

\"BorderFile\": \"en/EN_Metric_A3_Border_Wide.svg\",

\"DrawingAreas\": \[

{

\"Top\": 11.0,

\"Left\": 21.0,

\"Width\": 194.0,

\"Height\": 110.0,

\"ViewDirection\": 2,

\"PrintScale\": 0.0,

\"Fit\": false,

\"UseCommonScale\": true

},

{

\"Top\": 11.0,

\"Left\": 215.0,

\"Width\": 194.0,

\"Height\": 110.0,

\"ViewDirection\": 3,

\"PrintScale\": 0.0,

\"Fit\": false,

\"UseCommonScale\": true

},

{

\"Top\": 121.0,

\"Left\": 21.0,

\"Width\": 194.0,

\"Height\": 110.0,

\"ViewDirection\": 4,

\"PrintScale\": 0.0,

\"Fit\": false,

\"UseCommonScale\": true

}

]
```

}

\]

## Create a Link File with Excel

You can create link files in Excel. Simply include columns for **Condition**, **Link**, and **Name**. In the **Condition** column, you can use any attribute. To select all objects, use `*` as a wildcard, for example `Task=*`.

**Example:**

<table>
<tr><th>Condition</th><th>Link</th><th>Name</th></tr>
<tr><td>

Task=Piping

</td><td>

https://www.caxperts.com/

</td><td>

Homepage

</td></tr>
<tr><td>

Task=Structure

</td><td>

https://en.wikipedia.org/wiki/Structure

</td><td>

Wikipedia

</td></tr>
</table>

## Report Definition Files

<table>
<tr><td>

<img src="./media/ButtonReport.png" width="44" height="44">

</td><td>

After clicking **Create a report** in **File management**, you can choose a report definition file or create a new report. Report definition files are Excel files that must be stored in the `Data` folder of your UDiTH directory. By default, saved reports follow this naming convention:

</td></tr>
</table>

> `Report name (1)_YYYY-MM-DD_HH_MM`

<img src="./media/image182.png" width="721" height="109">

A report definition file can contain the following columns:

<table>
<tr><td>

Name

</td><td>

A descriptive name for your report.

</td></tr>
<tr><td>

Description (optional)

</td><td>

Additional description explaining the purpose of the report.

</td></tr>
<tr><td>

Filter (optional)

</td><td>

You can use all attributes including custom attributes.

</td></tr>
<tr><td>

Scope (optional)

</td><td>

Choose "All", "Selected" or "Visible".

</td></tr>
<tr><td>

Column (optional)

</td><td>

The attribute column that should be used for sorting your report.

</td></tr>
<tr><td>

Sort (optional)

</td><td>

You can sort in **ascending (asc)** or **descending (des)** order. If no sorting order is specified, UDiTH sorts in ascending order. Example: `descending:Name`

</td></tr>
<tr><td>

Forbidden (optional)

</td><td>

Enter actions that you do not want to allow for your reports: **C** (customise), **F** (filter), **S** (save). Combine them using a comma or semicolon. Example: `C,F;S`

</td></tr>
<tr><td>

Attributes

</td><td>

The attributes that the report should contain. **Colour** returns the current colour, and **Count** returns the number of objects contained within that object. For example, a vessel with four nozzles has a count of five.

</td></tr>
</table>

---

# UDiTH App — Appendix - Command line parameters

# Command Line Parameters

<table>
<tr><th>Command</th><th>Description</th></tr>
<tr><td>

model

</td><td>

Path or URL of the model to load at start-up.

</td></tr>
<tr><td>

forcenewinstance

</td><td>

Forces the creation of a new instance, even when the same model is already loaded.

</td></tr>
<tr><td>

forceapicall

</td><td>

Prevents the creation of a new process and only attempts to send the API command.

</td></tr>
<tr><td>

apitcpport

</td><td>

The TCP port used by the API.

</td></tr>
<tr><td>

silent

</td><td>

Hides the splash screen at start-up.

</td></tr>
<tr><td>

nocrashmessage

</td><td>

Hides the message shown in the event of an unexpected crash.

</td></tr>
<tr><td>

softwarerenderer

</td><td>

Uses the software renderer.

</td></tr>
<tr><td>

hwnd

</td><td>

Renders into a window identified by an `HWND`.

</td></tr>
</table>

---

# UDiTH App — Appendix - Performance optimisation

# Appendix - Performance Optimisation

## Best Practice for Optimal Performance

There are several ways to optimise performance in UDiTH. This document is intended to help you find the best solution for your specific needs.

### Optimising the Model and Data

UDiTH is able to handle models and sessions of virtually any size, but performance depends on the device configuration in use. Depending on the operating system, CPU, GPU, and available RAM, there may be restrictions or limits affecting the use of UDiTH, just as with any other software.

In such cases, we recommend optimising your model with particular attention to the following:

#### Attribute Information

Although the number of attributes plays only a minor role in the overall model size, it can still affect overall performance. There are several ways to improve performance:

1. Avoid unnecessary attributes

   Many authoring systems create attributes that have no informational value for the user, such as colour, shading, and other information relevant only to the authoring system. By removing these attributes from the session in UDiTH Builder, you can streamline the information available in your session. The fewer attributes there are, the easier it is to navigate in UDiTH and the faster search and other functions perform.

2. Fixed attributes before custom attributes

   If you do not need to change attribute values while UDiTH is running, it is preferable to use the SDK import functions, for example the ExcelImport plugin, instead of Custom Attributes. Custom Attributes are loaded when the model is opened and can noticeably increase loading time if they are used extensively.

3. Optimise attribute hierarchy

   In UDiTH, you can define a custom hierarchy, allowing you to streamline the structure of your data. This can improve model performance and increase the speed at which attributes are displayed when object selections change.

#### Model Scope

Especially on mobile devices with more restrictive memory management, the scope of the model can affect performance. For such devices, we recommend using the package functionality in UDiTH Builder to divide the full model into smaller packages, while still allowing users to view those packages separately on their mobile device.

### Performance Options

The UDiTH settings include several options for adjusting viewer performance, affecting loading, caching, and shading of the model:

- Render quality level

  - Low - displays the model without shading. Recommended for the best performance.
  - Medium - displays simple shading. Recommended for overlapping surfaces.
  - High - provides the highest quality, including shadows. This also has the highest resource consumption.

- Triangles limit

  Defines the number of triangles displayed at the same time. The more triangles that are displayed, the better the visual quality, but the more performance is required.

- Memory mesh cache size (triangles)

  This setting defines the number of triangles kept in memory. It therefore affects RAM usage. Setting the memory mesh cache size to `0` disables caching, which may result in slightly longer reloading times but reduces memory usage on the viewing device.

- Memory model cache size (bytes)

  Sets the maximum amount of RAM available to the model. This setting controls memory usage and can be adjusted to suit devices with less available memory.

We recommend adjusting these settings either to values that suit most of your devices or, for maximum compatibility, to the lowest common setting. Users with more powerful workstations or devices, especially in terms of CPU, GPU, and RAM, can adjust these values individually and save their settings for later manual loading.

### The Alternatives

#### Upgrade the Hardware

> One alternative is to upgrade the hardware on your devices, for example the CPU, GPU, or RAM in workstations and desktops, or to use better-equipped mobile devices. This often leads to significantly better performance. When acquiring new hardware, consider these parameters for optimum performance.

#### Browser-Based Viewing

Alternatively, if none of the measures above leads to the expected results, you can use our browser-based viewing technology. This enables you to view your model in any industry-standard web browser without installation, plugins, or other requirements, apart from an internet or intranet connection to the server. The computing workload is moved to the cloud or to your corporate intranet. Although this requires powerful server-side hardware, it enables virtually any device to view models of any size at high performance.

---

# UDiTH App — Appendix - How to use 3D PDF files

# How to Use 3D PDF Files

The 3D PDF file created by UDiTH Builder can be opened with **Adobe Reader**.

<img src="./media/3DPDF_image7.png" width="756" height="455">

## 3D Toolbar

### Display and Hide 3D Toolbar

<img src="./media/3DPDF_image8.png" width="606" height="27">

-   Click the 3D model with the **Hand** tool to display the 3D toolbar.

    The 3D toolbar always appears above the upper-left corner of the 3D model and cannot be moved.

-   Hide the toolbar by right-clicking the 3D model and choosing **Hide toolbar**. To show the toolbar again, choose **Show toolbar** from the same context menu.

-   You can expand and collapse the 3D tools shown on the toolbar by using **Tools → Expand/Collapse 3D tools** in the context menu.

> All tools that can be used from the 3D toolbar can also be used by right-clicking the 3D model and using the different items of the context menu.

## Use 3D Navigation Tools

-   **Rotate (<img src="./media/3DPDF_image9.png" width="16" height="16">):**

    Turns 3D objects relative to the screen. How the objects move depends on the starting view, where you begin dragging, and the direction in which you drag, for example in a straight line, a curve, a circle, or a loop.

> You can also use the **Hand** tool to select an object if **Enable selection for the hand tool** is selected in the 3D preferences.

-   **Spin (<img src="./media/3DPDF_image10.png" width="16" height="16">):**

    Rotates a 3D model in parallel with two fixed axes in the 3D model, the x-axis and the z-axis.

-   **Pan (<img src="./media/3DPDF_image11.png" width="16" height="16">):**

    Moves the model vertically and horizontally only. You can also pan with the **Hand** tool by pressing **Ctrl** while dragging.

-   **Zoom (<img src="./media/3DPDF_image12.png" width="16" height="16">):**

    Moves you towards or away from objects in the scene when you drag vertically. You can also zoom with the **Hand** tool by holding down **Shift** while dragging.

-   **Walk (<img src="./media/3DPDF_image13.png" width="16" height="16">):**

> The **Walk** tool is only available when you select the 3D Preferences setting that consolidates tools or when you right-click the 3D model and choose **Tools** → **Walk**.

    Pivots horizontally around the scene when you drag horizontally, moves forwards or backwards through the scene when you drag vertically, and maintains a constant elevation level regardless of how you drag. The **Walk** tool is especially useful for architectural 3D models.

-   Drag with the right mouse button to change how several of the 3D navigation tools behave:

    For the **Rotate** and **Spin** tools, right-click dragging temporarily switches to the **Zoom** tool.

    For the **Zoom** tool, it behaves like a **Marquee zoom** tool, zooming to the area that you define while dragging.

    For the **Walk** tool, right-click dragging makes the tool behave like the **Pan** tool.

### Use 3D Toolbar View Controls

-   **Default view** (<img src="./media/3DPDF_image14.png" width="16" height="16">):

    Returns to a preset zoom, pan, rotation, and projection mode for the 3D model.

> If an object ever moves out of your view, click the **Default view** icon on the 3D toolbar to move the object back into view.

-   **Views menu:**

    Selects the different views defined for the current 3D model.

-   **Toggle model tree (<img src="./media/3DPDF_image15.png" width="16" height="16">):**

    Opens and hides the model tree.

-   **Projection (<img src="./media/3DPDF_image16.png" width="16" height="16">):**

    Toggles between perspective and orthographic projection of the 3D object.

    Orthographic projection effectively removes a dimension, preserving the size ratio between objects but giving the 3D model a less realistic appearance.

-   **Model render mode menu (<img src="./media/3DPDF_image17.png" width="16" height="16">):**

    Selects the different model render modes so that you can see how the 3D shape appears. Compare the differences in rendering speed and quality.

    The model rendering modes include combinations of factors that affect the appearance of the 3D object. The illustration below shows a simple tank object rendered in each of the available modes. The default rendering mode is usually **Solid**.

**Table 1: Model rendering modes**

<table>
<tr><th>A. Solid</th><th>B. Transparent Bounding Box</th><th>C. Transparent</th></tr>
<tr><td>

<img src="./media/3DPDF_image18.png" width="208" height="213">

</td><td>

<img src="./media/3DPDF_image19.png" width="208" height="213">

</td><td>

<img src="./media/3DPDF_image20.png" width="208" height="213">

</td></tr>
<tr><th>D. Solid Wireframe</th><th>E. Illustration</th><th>F. Solid Outline</th></tr>
<tr><td>

<img src="./media/3DPDF_image21.png" width="208" height="213">

</td><td>

<img src="./media/3DPDF_image22.png" width="208" height="213">

</td><td>

<img src="./media/3DPDF_image23.png" width="208" height="213">

</td></tr>
<tr><th>G. Shaded Illustration</th><th>H. Bounding Box</th><th>I. Transparent Bounding Box Outline</th></tr>
<tr><td>

<img src="./media/3DPDF_image24.png" width="208" height="213">

</td><td>

<img src="./media/3DPDF_image25.png" width="208" height="213">

</td><td>

<img src="./media/3DPDF_image26.png" width="208" height="213">

</td></tr>
<tr><th>J. Wireframe</th><th>K. Shaded Wireframe</th><th>L. Transparent Wireframe</th></tr>
<tr><td>

<img src="./media/3DPDF_image27.png" width="208" height="213">

</td><td>

<img src="./media/3DPDF_image28.png" width="208" height="213">

</td><td>

<img src="./media/3DPDF_image29.png" width="208" height="213">

</td></tr>
<tr><th>M. Hidden Wireframe</th><th>N. Vertices</th><th>O. Shaded Vertices</th></tr>
<tr><td>

<img src="./media/3DPDF_image30.png" width="208" height="213">

</td><td>

<img src="./media/3DPDF_image31.png" width="208" height="213">

</td><td>

<img src="./media/3DPDF_image32.png" width="208" height="213">

</td></tr>
</table>

-   **Enable extra lighting menu**
    (![](./media/3DPDF_image33.png)):

    Uses the different lighting effects so that you can see how the number, colour, orientation, and brightness of the lights, together with the reflectivity of the surface and other factors, affect the illumination of the 3D object.

-   **Background colour swatch:**

    Opens the colour picker, which you can use to select a different colour for the area surrounding the 3D object.

-   **Toggle cross section (<img src="./media/3DPDF_image34.png" width="16" height="16">), (not available in Adobe Reader):**

    Shows and hides cross-sections of the object. Click the pop-up button to open the **Cross section properties** dialogue box.

-   **Camera properties (<img src="./media/3DPDF_image35.png" width="16" height="16">):**

    Opens the camera properties window and allows you to modify the camera position. **Save camera view** is not available in **Adobe Reader**.

 <img src="./media/3DPDF_image36.png" width="292" height="555">

## Model Tree

### Display and Hide Model Tree

-   Open the model tree by clicking the **Toggle model tree** button (<img src="./media/3DPDF_image15.png" width="16" height="16">) on the 3D toolbar, or by right-clicking the 3D model and choosing **Show model tree**.

> You can change the default behaviour of the model tree by right-clicking the 3D model, choosing **3D preferences**, and then selecting an option from the **Open model tree on 3D activation** menu.

    The model tree appears in the navigation pane on the left side of the work area.

    The model tree has three panes: the Structure pane, the View pane, and the Object Data pane. Each displays a specific type of information or controls.

### Use Structure Pane

The top pane shows the tree structure of the 3D object, which is made up of individual parts. For example, a 3D object showing a storage tank may have separate groups of objects, called nodes, for the tank, insulation, and nozzles.

To configure the model tree and attributes in the PDF file, see the section *3D PDF settings*.

-   Move through the hierarchy and see how the related parts in the 3D model are selected when you select the tree nodes.

-   Hide various parts, to isolate others, by removing checkmarks next to them.

-   Highlight, view, or modify the 3D model content at any level in the model tree by right-clicking a node and selecting the required function.

<img src="./media/3DPDF_image37.png" width="400" height="517">

-   Select the **Options** menu and choose any of the following:

> **Model render mode:** Changes the surface appearance of the entire 3D
> model according to the item you choose from the submenu: Transparent
> Bounding Box, Solid, Transparent, Solid Wireframe and so on.
>
> **Show all parts:** Displays the entire 3D model.
>
> **Fit visible:** Displays all visible parts and centres them in the
> view.
>
> **Display bounding box:** Displays the box that encloses the 3D object
> or selected parts of the model.
>
> **Set bounding box colour:** Changes the colour of the bounding box.
> Choose this option, select a colour, and then click OK.
>
> **Hide:** Displays the model without showing the selected parts.
>
> **Isolate:** Displays only the selected part, hiding all others.
>
> **Zoom to part:** Changes the centre focus from the entire 3D model to
> the selected parts. This setting is especially useful for rotating a
> part, allowing the rotation to occur around the part's centre focus
> rather than that of the entire model.
>
> **Transparent:** Displays a see-through version of the selected part.
>
> **Export as XML:** Creates a separate XML file of either whole tree or
> current node of the 3D model.

> The items that appear on the **Options** menu and the order in which they are listed depend on whether the selected 3D model is composed of just one part or multiple parts.

### Use View Pane

The middle pane lists the views that the document author has defined for that 3D object.

-   After you changed the view, such as by isolating and rotating a part, simply click one of the listed views to return the 3D model to a saved state.

-   Change the default view (<img src="./media/3DPDF_image14.png" width="16" height="16">) in the **3D toolbar** view controls by right-clicking a view in the **View pane** and then choosing **Set as default view**.

### Use Object Data Pane

The lower pane displays other information, including properties and metadata, if any, about the object or part.

-   Click a particular object in the 3D view. The Object Data pane is filled in automatically with the properties of the selected object, for example SmartPlant Review properties.

<img src="./media/3DPDF_image38.png" width="304" height="215">

The selected object is highlighted in a bounding box and the attributes are shown on the object data pane:

<img src="./media/3DPDF_image39.png" width="290" height="242">

### Volume Clipping

3D PDF files created by CAXperts **Universal Plant Viewer** contain a volume clipping feature.

-   Use the context menu on a 3D object in Acrobat Reader and click **CAXperts - Volume clipping**.

<img src="./media/3DPDF_image40.png" width="289" height="423">

Then enter the volume clipping radius around the selected object:

<img src="./media/3DPDF_image41.png" width="229" height="188">

> All CAXperts menu items use JavaScript. This is not dangerous, so you can ignore the **Warning: JavaScript window** message.

-   Finally, press **OK**. Only the selected object and all objects within the specified radius will remain visible.

 <img src="./media/3DPDF_image42.png" width="424" height="426">

To switch off volume clipping, click **CAXperts - Reset model** in the context menu.

### Measure with CAXperts - Measuring Tool

-   Use the context menu on a 3D object in Acrobat Reader and click **CAXperts - Measuring tool** to measure 3D models.

-   Click the first object after this message appears:

 ![](./media/3DPDF_image43.png)

-   Then click on the second object:

 ![](./media/3DPDF_image44.png)

 The distance between the centres of the bounding boxes will be shown:

 ![](./media/3DPDF_image45.png)

### Link 2D Objects with 3D Objects

The function to link 2D objects with objects of the 3D model is only available in PDF documents created with CAXperts tools.

-   Show the 3D model in a floating window by right-clicking the 3D model and choosing **View in floating window**.

-   Click a linked item in the 2D drawing to make the 3D window show the item's 3D representation. Links are indicated by the mouse pointer changing to a hand whenever you hover over a link.

<img src="./media/3DPDF_image46.png" width="526" height="388">

## 3D Models in Acrobat Professional

### Measure 3D Objects

-   Use the **3D measurement tool** to measure 3D models.

    You can create measurements between combinations of points or edges on the 3D model.

-   Move the pointer over the 3D model to highlight specific points and edges.

> The **3D measurement tool** supports four types of measurements:

1.  perpendicular distance between two straight edges,

2.  linear distance between two points,

3.  the radius of circular edges, and

4.  the angle between two edges (or three points).

You can also display comments while taking measurements. However, these comments, also called measurement mark-ups, are not preserved after the document is closed.

<img src="./media/3DPDF_image47.png" width="344" height="232">

Figure 1: 3D measurement display

1.  Click a 3D model in a PDF to enable it.

2.  Click the **3D measurement tool** icon
    (![](./media/3DPDF_image48.png)) on the 3D toolbar. (If the 3D
    toolbar view is set for consolidated tools, the **3D measurement
    tool** is available on the pop-up menu under the **Rotate**,
    **Spin**, **Pan**, **Zoom**, or **Walk** tool.)

3.  Select the required options in the **Snap enables**, **Measurement types**, and **Units and mark-up settings** areas of the **3D measurement tool** palette.

4.  Under **Units and mark-up settings**, change the options as required. Leave the **3D measurement tool** palette open.

5.  Measure the 3D model:

-   To measure the distance between two positions on the 3D model, click to set a start point and then move the pointer to another location or edge.

-   To measure the circumference of a round shape, move the pointer to the edge of the shape so that a circle appears, and click once.

-   To create and set the position of an annotation on the measurement, select **Measurement mark-up** in the **3D measurement tool** palette and then type a mark-up message in **Annotation**. Measure the 3D model as described above, but click to set the end point for the measurement and then click a third time to set the location of the measurement and annotation text.

-   To discontinue a measurement, right-click and choose **Cancel measurement**.

-   To delete a measurement mark-up, click it with the **3D measurement tool** and press **Delete**.

> While measuring, press **Ctrl+Shift** to rotate. Hold down **Ctrl** to pan, hold down **Shift** to zoom, and hold down **Alt** to disable snap.

**Snap enables** options in the **3D measurement tool** palette:

-   **3D snap to edge endpoints (**![](./media/3DPDF_image49.png)**):** Snaps to the entire edge.

-   **3D snap to linear edges (**![](./media/3DPDF_image50.png)**):** Snaps to a straight-line
    segment of an edge.

-   **3D snap to radial edges (**![](./media/3DPDF_image51.png)**):** Snaps to a circumference.

-   **3D snap to silhouettes (**![](./media/3DPDF_image52.png)**):** Snaps to the apparent edge of
    a part, such as the side of a cylinder.

-   **3D snap to planar faces (**![](./media/3DPDF_image53.png)**):** Snaps to the geometric plane
    making up a face of the part.

**Measurement types** options in the **3D measurement tool** palette:

-   **3D point to point measurement (**![](./media/3DPDF_image54.png)**):** Measures the distance between two positions on the 3D model that you click to set a start point and then click another location to set an end point or edge.

-   **3D perpendicular dimension (**![](./media/3DPDF_image55.png)**):** Measures the distance between two edges taken at a right angle to the starting edge.

-   **3D radial dimension (**![](./media/3DPDF_image56.png)**):** Measures the radius at the location clicked.

-   **3D angle measurement (**![](./media/3DPDF_image57.png)**):** Measures the angle between two edges.

**Units and mark-up** options in the **3D measurement tool** palette:

> If you do not see these settings, choose **Show details** on the palette's **Options** menu.

-   **Model units scale ratio:** Shows the relationship between units in the model and real object measurements. Use the **Display units** menu to select a different unit for the real object measurements.

-   **Measurement mark-up**: Select to have the measurements appear as comments in the PDF.

-   **Label:** Type text that you want to appear with the measurement, both in the 3D model area and in the comments panel. (Not available if **Measurement mark-up** is not selected.)

**3D Measurement tool** viewing options

Use the **Options** menu in the **3D measurement tool** palette to set the viewing options.

> The **3D measurement tool** palette changes to the **Distance tool** palette after a brief delay when the pointer moves outside the canvas area of the 3D model. Moving the pointer back over the 3D model restores the **3D measurement tool** options. The **Ortho** option is available only for the **2D Distance**, **Perimeter**, and **Area** tools.

-   **Show details:** Shows or hides the **Cursor location** and **Units and mark-up settings** options in the **3D measurement tool** palette.

-   **Show rulers:** Shows or hides vertical and horizontal rulers on the page. (Has the same effect as choosing **View** → **Rulers**.)

-   **Snap to 2D content;** Ensures precise measurement in 2D objects.

-   **Snap to 3D content:** Ensures precise measurement in 3D objects.

-   **3D measurement navigation tips:** Opens a dialog box with keyboard shortcuts for several 3D features. You can use these shortcuts while you are measuring.

#### Measuring Preferences

Change the 3D measuring preferences to determine how 3D data is measured. These options appear in the **Measuring (3D)** panel of the **Preferences** dialog box.

> These preferences apply to PDFs that have commenting enabled.

**Use scales and units from model (when present):** Displays measurements based on the model units, if present, taken from the original 3D model. Deselect this option to specify the measurement units manually. This setting can be changed in the **3D measurement tool** palette.

**Use default display unit:** Uses units of measurement that you specify here rather than those in the 3D model.

**Significant digits to display:** Specifies the maximum number of digits in the measurement number.

**3D measuring line colour:** Specifies the colour of the line that appears when you click or drag to measure an object.

**Measure feedback size:** Sets the text size for the measurement display.

**Angular measurements shown in:** Specifies units as either degrees or radians.

**Circular measurements shown as:** Specifies whether the diameter or radius is measured for circular parts.

**3D snap settings:** Turns on snap and specifies whether points, arcs, edges, silhouette edges, or faces are snapped to. Sensitivity indicates how close the pointer needs to be to the item being snapped to. For **Snap hint colour**, specify the colour of the snap line that appears when you hold the pointer over the 3D object.

### Comment on 3D Designs

Comments added to a 3D object are associated with specific views that are defined when the comments are created.

If the view is changed, for example if the 3D object is rotated or moved, the comments are no longer visible.

> Adding comments to 3D model views requires version 7.0.7 or later of **Adobe Acrobat** or **Adobe Reader**.

If you don't want a comment to be associated with a 3D view, add the comment to another part of the page, outside the 3D object area.

#### Add comments to a 3D object

-   Select a tool from the **Comment & mark-up** toolbar.

> The text editing tools have no effect on 3D objects.

-   Click inside the 3D object area to create a new comment and a new view definition in the model tree with a default name such as `CommentView1`.

To add more comments, do one of the following:

-   To create an additional comment in a view, make sure that the required commenting view is selected in the Model Tree, and then click inside the 3D object area.

-   To create an additional comment in a new commenting view, make sure that no commenting view is selected in the Model Tree, and then click inside the 3D object area.

> If one of the automatically generated commenting views is deleted, the associated comments are still available. They can be viewed and selected in the Comments panel or in the model tree, where they are listed under **Views**. Selecting a comment switches the 3D model back to the same viewing configuration it had when the comment was added.

#### Display Comments for a 3D Object

Do one of the following:

-   In the model tree, select a view that contains comments.

-   Click the **Comments** button or choose **View** → **Navigation panels** → **Comments**.

-   In the view pane of the model tree, click **Options** and choose **List comments**.

-   Double-click a comment to open its comment window.

### Run a JavaScript

If there is a separate JavaScript file associated with the 3D model PDF, you can run it.

-   Right-click the 3D model in the PDF, and choose **Run JavaScript**.

## Acrobat 3D Preferences

In the 3D panel of the **Preferences** dialogue box (**Edit** → **Preferences**), you can determine whether the 3D toolbar and model tree are displayed by default. You can also specify a default renderer and decide whether animations are allowed.

**Preferred renderer:** Specifies the rendering engine used and therefore affects both performance and quality, so it is important to select the appropriate renderer. If you select a DirectX or OpenGL option, all rendering takes place using the graphics chip on the video card. If **Software** is selected, rendering may take longer, but performance may be more consistent with that of the model in its original application.

**Enable double-sided rendering:** Some model parts have two sides. To save time and space, you can deselect this option so that only the side facing the user is rendered. If the user looks inside a part rendered with only one side, the back side is invisible.

**Enable hardware rendering for legacy video cards:** Forces the use of a hardware accelerator for even video cards that do not support a pixel shader.

**Open model tree on 3D activation:** Determines whether the model tree is displayed when the 3D model is activated. Choose **Use annotation's setting** to use whichever setting the author selected when adding the 3D model to the PDF.

**Default toolbar state:** Determines whether the 3D toolbar is shown or hidden when a 3D model is activated. Choose **Use annotation's setting** to use whichever setting the author selected when adding the 3D model to the PDF.

**Enable 3D selection for the hand tool:** Lets the user select and highlight parts of the 3D model using the **Hand** tool. If this option is not selected, use the **Object data tool** (**Tools** → **Object data** → **Object data tool**) to select the object.

**Consolidate 3D tools on the 3D toolbar:** Selecting this option places the manipulation and navigation tools under the **Rotate** tool, thereby shortening the 3D toolbar.

**Enable view transitions:** Some 3D models include animated transitions between views. Deselect this option if you want to prevent this 3D animation.

**Optimization scheme for low frame rate:** Specifies what happens to animations of complex models when the frame rate becomes low.

-   **None:** Does not compromise the visuals and leaves the frame rate low.

-   **Bounding box:** Shows the three-dimensional planes enclosing the parts instead of the parts themselves, which keeps the frame rate high.

-   **Drop objects:** Does not show some parts of the model in order to keep the frame rate high.

**Frame rate threshold:** Sets the minimum frame rate, either by dragging the slider or entering a number in the value box. If the frame rate drops below that number of frames per second, the **Optimization scheme for low frame rate** option goes into effect.

---

# UDiTH Capture Station — Introduction

# Introduction

<img src="./media/Architecture_Update02-24.png" width="753" height="468">

UDiTH Builder uses 2D data, 3D data, and metadata prepared by the following adapters and the SDK to create UDiTH models:

<table>
<tr><th>CAXperts tool</th><th>Prerequisite</th><th>Converts</th></tr>
<tr><td>

AdapterForSmartReview

</td><td>

Intergraph SmartPlant Review

</td><td>

SmartPlant Review (.vue, .svf), PDS (.dri)

</td></tr>
<tr><td>

AdapterForNavisworks

</td><td>

Autodesk NavisWorks

</td><td>

Autodesk NavisWorks files (.nwc, .nwd, .nwf, etc.)

</td></tr>
<tr><td>

AdapterForE3d

</td><td>

</td><td>

AVEVA PDMS, E3D (.rvm, .att, .txt)

</td></tr>
<tr><td>

AdapterForIfc

</td><td>

</td><td>

BIM (.ifc)

</td></tr>
<tr><td>

AdapterForLaserScans

</td><td>

</td><td>

(.obj, .e57, .jpg)

</td></tr>
<tr><td>

AdapterForDocuments

</td><td>

Intergraph SmartSketch

</td><td>

Intergraph SmartSketch files (.sha)

PDMS/E3D Isometrics (.pdf, .hit)

PDF files (.pdf)

</td></tr>
<tr><td>

AdapterForDocuments

</td><td>

</td><td>

PDF files (.pdf)

</td></tr>
<tr><td>

AdapterForSpid

</td><td>

Intergraph SmartPlant P&ID, SmartSketch, Inkscape

</td><td>

Intergraph/Hexagon SmartPlant P&ID

</td></tr>
<tr><td>

AdapterForPdfPid

</td><td>

Inkscape

</td><td>

P&ID (.pdf)

</td></tr>
<tr><td>

SDK

</td><td>

Plugins

</td><td>

Additional data from any source

</td></tr>
</table>

## Prerequisites

### Software
- OS: Windows 10 64-bit or later
- AdapterForSmartReview: Recommended version: Intergraph Smart Review 2018 HF3 (SPR13.00.00.0441 2019-7-26)
- AdapterForDocuments: SmartSketch: 2018 or higher
- AdapterForNavisworks: Navisworks Simulate or Manage, version 2017 to 2023
- AdapterForSpid: SmartPlant P&ID 2009.5 or higher, SmartSketch 8.1 (2014 R1) or higher, Inkscape 1.4.3 or 1.4.4
- AdapterForPdfPid: Inkscape 1.4.3 or 1.4.4
- UDiTH Builder

### Hardware
- CPU: Intel Core i5, 6 cores, 3 GHz (Intel Core i7, 8 cores, 4 GHz or better recommended)
- RAM: 16 GB (32 GB recommended)
- Storage: HDD (SSD or NVMe SSD recommended)

### Other
Partitioning: OS on a separate drive, with source files, output, and the temp folder on the fastest drive.

For performance reasons, the files to be processed should be stored on a local drive.

## Quick start

1.  Determine which adapter is required for your input type, as shown in the table above.

2.  Start the required adapter.

3.  Select your input file.

4.  Click **Capture**.

<img src="./media/Builder_image9.png" width="648" height="273">

The UPVC file is then created.

5.  Start Builder.

    <img src="./media/Builder_image10.png" width="80" height="104">

6.  Open the UPVC file that was just created.

    <img src="./media/Builder_image11.png" width="602" height="343">

7.  Click **Generate output**.

    <img src="./media/Builder_image12.png" width="124" height="37">

8.  Select **UDiTH** as the output type.

    <img src="./media/Builder_image13.png" width="348" height="664">

9.  Click **Create**. The UPV model is then created.

10. Open the output folder and start `UDiTH.exe`.

**For mobile devices:** 

-   Copy the output folder to your web server.

-   Download and run the UDiTH app.

-   In the app, enter the address of your web server and model.

**For BrowserBasedViewing**

-   Open the UPV model website of your organisation directly in your browser.

---

# UDiTH Capture Station — Builder

# Builder

## Introduction

The **Builder** creates UDiTH 3D model data, and PID models, including attributes, project hierarchy, and 2D drawings (ISOs), from various sources using the output of the CAXperts adapters. It can also apply colour files and import Excel data.

The **Builder** cannot be used without the output files created by the adapters. Please refer to the adapter manuals for information on how to create those output files.

<img src="./media/Builder2_image2.png" width="632" height="426">

> A connection to `20.126.54.190` (`api365.slascone.com`) on port `443` is required. See [Licence upgrade and installation](https://help.caxperts.com/Licensing/Licence%20upgrade%20and%20installation).

## Model creation

<img src="./media/Builder2_image8.png" width="446" height="311">

This chapter explains how to create a 3D model.

### Input file / folder

Select the browse button to the right of **Input file** and choose one of the file types listed in the following table. To continue, you must select at least one valid file or a folder containing a valid file.
![](./media/Builder2_image9.png)

<table>
<tr><th>File ending</th><th>Meaning</th></tr>
<tr><td>

.upvc

</td><td>

A regular UPV database containing 3D objects.

</td></tr>
<tr><td>

.upvm

</td><td>

A merged UPV database created from multiple `.upvc` files.

</td></tr>
</table>

![](./media/Builder2_image10.png)

Select **Without 3D data** if you do not want a 3D model to be created and only want IntelliPID models.

> While browsing, press and hold **Ctrl** to select multiple `.upvc` or `.upvm` files. The result is a combined model consisting of all selected sub-models.

### Packages definition file

Select the browse button to the right of **Package definition file (optional)**. Click **Clear** to empty the input box. You do not need to select a package definition file in order to create models. To learn how to create and use packages, refer to the UDiTH documentation, chapter **Packages**.

![](./media/Builder2_image11.png)

Selecting **Complete model** creates the full model.

Selecting **Packages** creates one UPV model for each package in the package definition file, containing only the objects defined in those packages.

![](./media/Builder2_image12.png)
 
> Any `.xlsx` file can be selected when browsing for package files, but only files structured as described in the **Packages** chapter of the UDiTH documentation will work as intended.

### Generate output

<img src="./media/Builder2_image13.png" width="300" height="616">

After selecting the model files and any additional packages, click **Generate output** in the bottom-right corner of the Builder window to open **Output configuration**.

### Settings

![](./media/Builder2_image15.png)

The output folder is the location in which the model is generated.

The colour configuration file can be any `.xlsx` or `.txt` file.

The expiration date prevents the generated output from being used after the specified date.

> Any `.xlsx` file can be selected when browsing for colour files, but only files structured as described in the **Colour Files** chapter of the UDiTH documentation will work as intended.

### UDiTH settings

These settings are available only if the output type is **UDiTH**.
![](./media/Builder2_image16.png)

#### Display styles

**Display Styles Directory** makes all colour schemes contained in the selected folder available in the model as default display styles, without needing to select the colour files again.

#### Link mapping

**Link mapping file** creates links to external documents in the object attributes, as defined in the mapping file. Refer to the **Linking** chapter in the UPV documentation for information on how to create a link mapping file.

#### PID 3D mapping

The **PID <-> 3D mapping** file creates links between PID objects and their 3D counterparts. Refer to the AdapterForSpid documentation for information on how to create a PID 3D mapping file. A database must be selected in [IntelliPID database](#intellipid-database) for the mapping file to work. To create a PID 3D mapping file, an Excel sheet must contain the following cells:

<img src="./media/Builder2_image17.png" width="722" height="30">

`IntelliPidAttribute` and `3DAttribute` must contain the names of the attributes to be mapped. `IntelliPidRegEx` and `3DRegEx` must contain the regular expression commands required to make the values of the IntelliPid and 3D attributes match. Only character deletion is allowed; no characters may be added with the regular expression command.
<img src="./media/Builder2_image18.png" width="722" height="107">

> Any `.xlsx` file can be selected when browsing for link or PID mapping files, but only files structured as described in the relevant documentation will work as intended.

#### Reuse 2D/3D projections

The **2D <-> 3D mapping** source directory defines the location of `.upvf` file or files containing picture-based 2D/3D projections that should be made available again in the new UPV model. Images from 2D/3D projections stored in those `.upvf` files are converted directly into the UPV model.

![](./media/Builder2_image33.png)

When selected, user authentication is required for downloaded models.

![](./media/Builder2_image19.png)

When **Allow download** is selected, a user can download the model to a local machine instead of working only with a server-based version.

![](./media/Builder2_image20.png)

When selected, the view cube in UPV shows XYZ coordinates instead of compass directions. This is only the default view mode and can be changed later in the UPV settings.

## Tools settings

![](./media/Builder2_image22.png)

The **Tools Settings** provide many advanced configuration options for the generated output.

### General

<img src="./media/Builder2_image34.png" width="616" height="478">

#### Export caching

The **Export Caching** option enables the program to reuse already calculated laser scan data. If the same laser scan model is exported repeatedly, the program saves time by reusing already calculated information. Only changes generally need to be recalculated.

<img src="./media/Builder2_image23.png" width="616" height="478">

> This option works best with laser scan data, but 3D data is also supported.

> This option results in significantly larger data files in exchange for faster processing.

#### Configuration file

Select the initial configuration file for the UDiTH model. This can later be changed in UPV itself.

> Any `.ini` file can be selected when browsing for a configuration file, but only files structured as described in the **Configuration File** chapter of the UDiTH documentation will work as intended.

#### Aspects

Select or deselect if a specified aspect should be part of the 3D model.

### Tree

![](./media/Builder2_image24.png)

The tree hierarchy shown in UPV can be edited. Select attributes by clicking the checkbox next to them and then use the right arrow button to add them to the list, or the left arrow button to remove them. Objects with those attributes show them in the order in which they were added to the tree bar.

### Attributes

<img src="./media/Builder_image26.png" width="469" height="361">

Drag and drop attributes to or from **Used attributes** if they are to be shown in UPV when an object with the specified attribute is selected.
Multiple attributes can be moved by selecting the checkbox next to them and using the arrows between the lists.

### Mapping
Mapping allows the transformation, rotation, and scaling of multiple models.

<img src="./media/Builder_Mapping.png" width="569">

1.  These options are used for rotation and scaling. This step is always processed first.

    In this example, `Model1.upvc` rotates around `-100,50,0` metres. Always use metres as the unit.

2.  In the second step, the model is moved by the specified distances to its destination.

---
---


### Advanced Transformation of UPVC Files

The Builder provides an additional option to apply **translation, rotation, and scaling** to UPVC files during model creation.

### General Concept

In addition to the UPVC file, an **INI file** can be placed in the same directory.  
The INI file must have **exactly the same filename** as the UPVC file.

If present, the Builder will automatically read transformation parameters from this INI file and apply them when generating the UPV/UDiTH model.

---

### INI File Parameters

The INI file defines the transformation using the following parameters:

- **Point of Rotation**  
  Defined in SI units \([m]\)

- **Rotation Angle**  
  Defined in degrees \([deg]\)  
  - Positive values = counterclockwise rotation  
  - Rotation can be applied around all axes

- **Translation**  
  Defined in X, Y, and Z directions
  
  Example of INI files next to UPVC files:

<img src="./media/Builder_AdvancedTransformation1.png" width="172">

Example of INI file:
```ini
[rotation] 
# Point of Rotation in SI-unit [m]
# Rotation Angle in [deg] - positive value = CounterClockWise 
# Only rotation around one axis is possible 
pointX=45
pointY=6
pointZ=0
AngleX=0
AngleY=22.5
AngleZ=-45
[translation]
X=30.125
Y=-15.369
Z=0
[scale]
scale=1.5
```

---

### Logging

All applied transformations are fully documented in the **Builder log file**.  
This includes the complete transformation applied to the UPVC during processing.
<img src="./media/Builder_AdvancedTransformation3.png" width="746">


---

### Interaction with UPVM Files

The existing **UPVM-based transformation concept** remains supported.

However, the following rules apply:

- If an INI file exists alongside the UPVC file (same name),  
  **the INI transformation values take precedence**
- In this case, **UPVM transformation data is ignored**
- This behavior also applies to **batch processing**



---

### Summary

- Optional INI file enables direct transformation control per UPVC file
- Supports rotation (all axes), translation, and scaling
- INI overrides UPVM if both are present
- Fully traceable via Builder log output

---
---

><img src="./media/Builder_Icon_Info2.png" width="20"> **Use as catalog** is not applicable yet.
---
---

### Advanced options

<img src="./media/Builder2_image27.png" width="469" height=""> 

#### Document databases

Document databases are `.upvd` files. Add or remove the required files as needed. Refer to the AdapterForDocuments documentation for information on how to create document database files.

**Include all drawings:** All drawings are included in the model, regardless of whether they contain a reference to an object in the model.

#### IntelliPID database

IntelliPID databases are `.upvp` files. Add a single file. To connect PID and 3D objects, also add a PID 3D mapping file as described in [PID 3D mapping](#pid-3d-mapping).

### Plugins

<img src="./media/Builder2_image28.png" width="463" height="323">

Plugins are C# libraries created with the CAXperts plugin API. To move plugins into or out of **Used plugins**, select the checkbox next to the plugin and use the arrow buttons between the lists.

The plugins themselves, together with their config files if required, must be stored in the local `%AppData%` folder of the machine running UPVBuilder.

### Command-Line Parameter Guide

1. **Prepare your files** - Ensure that you have the required input files ready.

<table>
<tr><th>Parameter</th><th>Example</th><th></th></tr>
<tr>
<td>

<b>```-input:```</b>

</td><td>

```C:\Input\Example.upvc```

</td><td>

Path to the file or directory that you want to convert into a UPV file.

</td></tr>
<tr><td>

<b>```-expirationdate:```</b>

</td><td>

```"2099-31-12T23:59:59.9999999" OR "+30"```

</td><td>

Sets an expiration date for the exported file. You can specify either a fixed date or a duration in days.

</td></tr>
</table>

2. **Configure additional settings** - Customise colour configurations, configuration files, IntelliPID mapping files, and similar options.
<table>
<tr><th>Parameter</th><th>Example</th><th></th></tr>
<tr><td>

<b>```-output:```</b>

</td><td>

```C:\Output\Example\Output```

</td><td>

Path where the model files are saved.

</td></tr>
<tr><td>

<b>```-color:```</b>

</td><td>

```C:\Settings\Color.xls```

</td><td>

Path to a file that contains colour configurations.

</td></tr>
<tr><td>

<b>```-config:```</b>

</td><td>

```C:\Settings\Example.ini```

</td><td>

Path to the configuration file used for processing.

</td></tr>
<tr><td>

<b>```-intellipidmapping:```</b>

</td><td>

```C:\Settings\IntelliPidMapping.xlsx```

</td><td>

Path to a file used for IntelliPID mapping.

</td></tr>
<tr><td>

<b>```-package:```</b>

</td><td>

```C:\Input\PackagesDefinitionFile.xlsx```

</td><td>

Path to a file defining package definitions.

</td></tr>
<tr><td>

<b>```-disablesearchindex:```</b>

</td><td>

</td><td>

Disables creation of a search index file.

</td></tr>
<tr><td>

<b>```-outputplugins:```</b>

</td><td>

</td><td>

Outputs registered plugin names in the result.

</td></tr>
<tr><td>

<b>```-disablepdfcache:```</b>

</td><td>

</td><td>

Disables creation of PDF cache entries.

</td></tr>
<tr><td>

<b>```-pdfcachemaxdp:```</b>

</td><td>

```512```

</td><td>

Determines the highest DPI used for PDF caching. Allowed values are `128`, `512`, and `1024`.

</td></tr>
<tr><td>

<b>```-includealldrawings:```</b>

</td><td>

</td><td>

Specifies whether all drawings or only linked drawings should be included.

</td></tr>
<tr><td>

<b>```-plugin:```</b>

</td><td>

```ExamplePluginName```

</td><td>

Enables a specific plugin.

</td></tr>
<tr><td>

<b>```-drawing:```</b>

</td><td>

```C:\Input\Example.upvd```

</td><td>

Path to a drawing file.

</td></tr>
<tr><td>

<b>```-intellipiddatabase:```</b>

</td><td>

```C:\Settings\IntelliPidDatabase.upvp```

</td><td>

 Path of an IntelliPID database file.

</td></tr>
</table>

5. **Execute the Command** - Open your command prompt or terminal. Navigate to the directory where the Universal Plant Viewer Builder executable is located.

6. **Run the Command** - Use the Universal Plant Viewer Builder command followed by the desired parameters.

7. **Monitor Progress** - Keep an eye on the progress in the command prompt or terminal.

8. **Review Output** - Check the output directories for your generated files. Optionally, review the log file for any error messages or additional information.

9. **Optional Steps**
>- Activate a license key using the <b>```-licensekey```</b> parameter.
>- Specify a custom log file path using the <b>```-log```</b> parameter.

---

# UDiTH Capture Station — UniversalPlantCreator

# UniversalPlantCreator

## Introduction

**UniversalPlantCreator** is a tool designed to generate models for **UDiTH** efficiently.

### Supported Model Formats

- **.ifc** (Industry Foundation Classes)
- **PDS .dri** (Intergraph's PDS data file)
- **Autodesk Navisworks** `.nwc`, `.nwd`
- **Smart3D .vue, .svf** (Smart Review formats)
- **PDMS/E3D .rvm** (PDMS/E3D model files)
- **Laser scans .e57** (3D point cloud data)
- **P&IDs .upvp** (UDiTH P&ID files)
- **Isometrics, drawings, and documents .upvd** (UDiTH document files)

## Installation and Setup

### UniversalPlantCreator Installation

- **Standalone Setup**: UniversalPlantCreator is a separate setup.
- **Adapter Validation**: The individual adapters are checked and validated by UniversalPlantCreator.
- **Additional Components**: The adapters and Builder must be installed separately.

## Basic Operations

### Drag & Drop Functionality

- **File Addition**: Drag and drop files or folders.
- **Output Folder Selection**: Select the output folder.

### Automatic File Processing

- **File Processing**:
  - UniversalPlantCreator automatically reads, validates, and processes `upvp`, `upvd`, colour, and package files when they are located in the same folder.

## Project Creation and Configuration

### Automatic Project Creation
- **Project Setup**: Creates UPV projects immediately using the predefined configuration files.

### Grouping and Ungrouping Models

- **Model Grouping**:
  - Two model inputs are automatically grouped into one model output.
  - You can select model inputs for grouping.
  - Two model outputs are possible.

## Advanced Features

### Command-Line Syntax and Batch File Generation

- **Batch File Creation**:
  - Generates command-line syntax or batch file input.

## System Requirements

### Software Requirements

- **Operating System**:
  - Minimum: Windows 7 or later, 64-bit
- **Smart Plant Review Models**:
  - Smart Review 2014R1 or later must be installed to work with `.vue` or `.svf` files.
- **Navisworks Models**:
  - Navisworks Simulate or Manage 2017 or later must be installed to handle Navisworks models.

### Hardware Requirements

- **CPU**:
  - Minimum: Intel Core i5, 4 cores, 3 GHz
- **RAM**:
  - Minimum: 4 GB
- **Storage**:
  - Minimum: HDD (SSD or NVMe SSD recommended for faster data processing and retrieval)

---

# UDiTH Capture Station — AdapterForSmartReview

# AdapterForSmartReview

**AdapterForSmartReview** captures 3D graphics and attributes from Intergraph Smart Review and stores them in a `*.upvc` file. This file is then used as input for CAXperts **UDiTH Builder** to generate UDiTH or 3D PDF sessions.

## Additional prerequisites

- Recommended version: Intergraph Smart Review 2018 HF3 (`SPR13.00.00.0441`, `2019-07-26`)
- Use Intergraph Smart Review 2020 only in exceptional cases, and only if no other option is possible.

## Using AdapterForSmartReview

The AdapterForSmartReview setup automatically detects the platform of Intergraph Smart Review, whether 32-bit or 64-bit, and installs the appropriate version of AdapterForSmartReview. The 64-bit version of AdapterForSmartReview is required in order to work with the 64-bit version of Smart Review.

Start **AdapterForSmartReview** and select an **input file**. The following Intergraph Smart Review file types are supported: `*.vue`, `*.svf`, and `*.dri`.

<img src="./media/AdapterForSmartReview_image5.jpg" width="549" height="239">

**Output file:** Defines the path to the `*.upvc` output file. This file contains all required information, such as graphics and attributes, needed to generate UDiTH or 3D PDF output using CAXperts **UDiTH Builder**.

If the `*.upvc` output file already exists, **AdapterForSmartReview** works in update mode and captures only the changes.

### Settings

All settings are stored automatically in the configuration file in the user profile. The location of this configuration file can be changed via **Tools** → **Settings**.

<img src="./media/AdapterForSmartReview_image6.jpg" width="372" height="331">

### General

**Configuration file:**

To export the settings, use **File** → **Save settings to file**:

<img src="./media/AdapterForSmartReview_image7.jpg" width="565" height="243">

To import settings from a file, use **File** → **Load settings from file**.

> **Load settings from file** does not change the current location of the configuration file. Once the settings have been imported, any further changes are stored in the current configuration file shown in the **Tools** → **Settings** menu.

**Aspects:**

All aspects from the SmartPlant Review session are captured automatically. No additional settings are required.

### Advanced

<img src="./media/AdapterForSmartReview_image8.jpg" width="515" height="431">

**Consider existing Display Sets** - If enabled, the visibility, for example whether objects are switched on or off in the Main View, and the colours are taken from the existing Smart Review Display Sets for `*.vue` files. This does not apply to `*.svf` session files.

**Maximum objects per capture group** - This is the maximum number of objects captured at one time. Higher values improve performance, but may also cause the Smart Review MDB file to overflow, as it is limited to a maximum size of `2 GB`. A value of `35000` is suitable for most sessions.

**Stroking tolerance** - Defines the quality of the exported graphics. Higher values mean better graphical quality, but they also increase the size of the exported graphics.

**Advanced Graphic Handling** - If this option is enabled, advanced measurement options become available in Universal Plant Viewer, for example snapping to edges.

**Use PDS Levels as Aspects** - Applicable only to DRI sessions. If enabled, every MicroStation level is treated as a separate aspect.

**Force attribute extraction** - Applicable when processing against an existing `*.upvc` file in update mode. If enabled, all attributes are extracted again, even when the individual objects in the existing `*.upvc` file are already up to date.

**Process modified vue-files completely** - This option mainly applies to `*.svf` sessions, where you want to reprocess complete `*.vue` files instead of individual objects within those files.

## Command Line Mode

> Due to restrictions in Smart Review, absolute paths must be used in AdapterForSmartReview command-line commands.

**AdapterForSmartReview** can also be used in command-line mode. Use the following command to display the available command-line arguments:

**C:\\\>"C:\\Program Files\\CAXperts\\AdapterForSmartReview\\AdapterForSmartReview.exe" /?**

The command-line arguments are shown after entering `/?`:

<img src="./media/AdapterForSmartReview_image9.jpg" width="773" height="224">

**AdapterForSmartReview** requires access to the desktop in order to run **Smart Review**. Therefore, to run the software as a scheduled task, the following steps are required:

1.  Download **<https://download.sysinternals.com/files/PSTools.zip>** and save it to disk, for example in `D:\PSTools\`.

2.  Create a BAT file, for example `D:\AdapterForSmartReview_Batch.bat`, which is used to run AdapterForSmartReview from the command line. Adjust the AdapterForSmartReview path and parameters as required:

>
> *D:\PSTools\psexec.exe -s -i 0 /accepteula "C:\Program Files\CAXperts\AdapterForSmartReview\AdapterForSmartReview.exe" -input:C:\Example.vue -output:C:\Output\Example.upvc*
>

-   `psexec.exe` executes AdapterForSmartReview in a separate console, session `0`. The software UI is not accessible or visible on the active desktop. To switch to session `0`, click the **View the message** button in the **Interactive Service Detection** window, which appears automatically after `D:\AdapterForSmartReview_Batch.bat` has started.

><img src="./media/AdapterForSmartReview_image10.png" width="468" height="294">

If this window does not appear, make sure that the **Interactive Services** service is enabled in the Windows Control Panel.

To return from the session `0` console to the main desktop, press the following button:

><img src="./media/AdapterForSmartReview_image11.png" width="360" height="219">

-   `D:\AdapterForSmartReview_Batch.bat` runs in a separate session. The active user can continue working in Windows or log off. Session `0` continues running in the background.

-   The `D:\AdapterForSmartReview_Batch.bat` script is executed under the `LocalSystem` account with limited permissions and without network access. Input and output files must therefore be located on a local drive. If you need network access, for example to copy the resulting `*.upvc` file to a network drive, add the corresponding commands to the end of `D:\AdapterForSmartReview_Batch.bat`.
    
    

## Advanced Features

### Advanced configuration for capturing graphics

This chapter is intended for advanced users. You can ignore it if advanced configuration is not required.

There are two types of `.ini` file: `User.ini` and one `.ini` file per project.
`User.ini` contains general settings that are valid for all projects. `Project.ini` contains only project-specific settings.

### Ignore objects (Project.ini)

The file size of the resulting PDF file depends noticeably on the number of objects captured. Ignoring less-meaningful objects (e.g. welds in **SmartPlant 3D** sessions) is one approach for reducing the overall 3D PDF file size.

The following syntax is used to define a custom object type in the `.ini` file:

```ini
[ObjectDefinition]
ObjectTypeName=AttributeName Operator Value
```

Example:

```ini
[ObjectDefinition]
MyWeld=Component=Weld
```

`[ObjectDefinition]` is the section name in the `.ini` file. `MyWeld` is the object type name and can be defined freely. `Component` is an attribute name that must exist in the design review session. The `=` character after `Component` is the operator. The operator can be `=` or `<>` for equality, or `like` and `not like` when using wildcards. `Weld` is the attribute value.

The example defines an object type `MyWeld` for objects whose attribute `Component` is equal to the value `Weld`. You can then create rules to ignore objects of type `MyWeld` using the following syntax in the `.ini` file:

```ini
[ObjectsToIgnore]
MyWeld=1
```

The `.ini` file is named `AdapterForSmartReview.ini` and is copied automatically from the installation template into the folder of the design review session loaded in **AdapterForSmartReview**. By default, the following definitions are included in the `.ini` file to ignore welds in **Smart 3D** sessions:

```ini
[ObjectDefinitions]
Weld= Component=Weld
Weld1= Commodity type Like *weld*
Weld2= Weld Type Like *weld*
Weld3= Type Like *weld*

[ObjectsToIgnore]
Weld=1
Weld1=1
Weld2=1
Weld3=1
```

## UseHierarchy (`User.ini`)

`UseHierarchy` is a user setting that reads the content of hierarchical labels in SPR.

`UseHierarchy=1` means that all hierarchical SPR labels are read from a `*.vue` file.

<img src="./media/AdapterForSmartReview_image18.jpg" width="642" height="492">

## SkipMissingModels

This setting applies only to the processing of `*.svf` sessions in batch mode. `*.svf` sessions can contain a large number of `*.vue` files. Missing individual `*.vue` files would normally stop the AdapterForSmartReview process. An internal default setting automatically skips missing `*.vue` files when the process is executed in batch mode or by using the UPC (UniversalPlantCreator).

## Log files

The software creates log files in the local `TEMP` directory, for example `C:\Temp\AdapterForSmartReview\`. You can also open these files by double-clicking the CAXperts **AdapterForSmartReview** status bar:

<img src="./media/AdapterForSmartReview_image12.png" width="682" height="394">

---

# UDiTH Capture Station — AdapterForNavisworks

# AdapterForNavisworks

**AdapterForNavisworks** captures 3D graphics and attributes from Autodesk Navisworks sessions and stores them in a `UPVC` file. This file is then used as input for the **Builder** to generate UPV models.

## Working with **AdapterForNavisworks**

## Prerequisites

**AdapterForNavisworks** works with Navisworks Simulate or Manage, versions 2020 to 2027.

After installing **AdapterForNavisworks**, check in Navisworks that the **CAXperts Adapter for Autodesk Navisworks Plugin** is available under **Tool add-ins**.

![](./media/AdapterForNavisworks_image5.png)

## Interactive Mode

Start **AdapterForNavisworks** and select either one **input file** or multiple **input files**. All file types supported by Navisworks are also supported by the adapter.

![](./media/AdapterForNavisworks_image6.png)

![](./media/AdapterForNavisworks_image7.png)

**Output file:** Defines the path to the `UPVC` output file. This file contains all required information, such as graphics and attributes, needed to generate UDiTH or 3D PDF output using CAXperts **UDiTH Builder**.

If the `UPVC` output file already exists, **AdapterForNavisworks** works in update mode and recaptures only those files whose last modified date has changed.

<table>
<tr><td>

**AdapterForNavisworks** includes a built-in mode for parallel processing of input files.

</td></tr>
<tr><td>

For faster processing, it is better to use multiple smaller files rather than one large file containing the same model.

</td></tr>
</table>

## Advanced Features

### Advanced configuration for capturing graphics

There are two types of `.ini` file: `User.ini` and one `.ini` file per project.
`User.ini` contains general settings that are valid for all projects. `Project.ini` contains only project-specific settings.

### 1. Ignore objects (Project.ini)

The size of the generated `UPVC` file is directly influenced by the number of captured objects. To reduce overall UDiTH model size, you can exclude non-essential objects such as welds, bolts, or washers.

#### Define Custom Object Types

Custom object types are defined in the `project.ini` file using the following syntax:
```ini
[ObjectDefinition]
<ObjectTypeName>=<AttributeName> <Operator> <Value>
```
**Parameters:**

- `<ObjectTypeName>`: Freely chosen name for the object type  
- `<AttributeName>`: Must match an existing attribute in the UPVC
- `<Operator>`: Supported operators:
  - `=` (equals)
  - `<>` (not equals)
  - `like` (supports wildcards, e.g. `*`)
  - `not like`  
- `<Value>`: Attribute value to match  

Default Configuration

The `Project.ini` file is automatically created in the %appdata%\CAXperts\Navisworks Adapter directory when the input files are processed.
This file contains predefined rules for ignoring objects related to welds. Since this is only an example, the value for [ObjectsToIgnore] is set to 0. The rule is activated with the option 1.

![](./media/AdapterForNavisworks_image71.jpg)

### 2. UseHierarchy (`User.ini`)

Only applicable for the converion of .vue-files of .svf-session files

`UseHierarchy` is a user setting that reads the content of hierarchical labels in SPR.

`UseHierarchy=1` means that all hierarchical SPR labels are read from a `*.vue` file.

![](./media/AdapterForNavisworks_image13.png)

### 3. Backup-UPVC

Back up the `UPVC` file before processing in update mode.

In the `user.ini` file, located by default in `%AppData%\CAXperts\Navisworks Adapter`, add the following entry:

```ini
[Settings]
BackupUPVC=1
```

![](./media/AdapterForNavisworks_image14.png)


### 4. Primitive Detection for Autodesk Revit Models

Based on specific attributes, **AdapterForNavisworks** can detect pipes and elbows in Revit-based models and replace them with lightweight primitive geometry. This helps reduce the overall triangle count in the model and is especially useful when UPV models are used on devices with limited graphics memory.

![](./media/AdapterForNavisworks_image15.png)

### 5. Collection of Attributes from Navisworks Models – default method

Navisworks can store attribute values at different levels in the Selection Tree. In UPV, all attributes are stored directly on the graphical element itself. By default, **AdapterForNavisworks** collects the attributes from the last three levels and stores them on the graphical element in UPV.

You can choose how many levels are used for attribute collection.

This behaviour can be controlled with an additional `user.ini` The `AddParentAttributes` parameter supports values from `1` to `15`, with a maximum of 15 levels for attribute processing.

> Higher settings than `3` can produce a very large number of attributes in many Autodesk-based files and can also increase model conversion times in **AdapterForNavisworks**.

![](./media/AdapterForNavisworks_image16.png)

### 6. Collection of Attributes from Navisworks Models – advanced method

There is another way to import attributes from NWD files. Using the “CategorySeparator” option, all attributes from the tabs in Navisworks are imported into the UDiTH model as separate categories.

CategorySeparator=/

![](./media/AdapterForNavisworks_image17.png)
![](./media/AdapterForNavisworks_image18.png)

### 7. Capture with Navisworks only
AdapterForNavisworks includes a built-in mode for converting SPR files for .vue and .svf formats. However, if you want to convert these file formats using the Navisworks Capture method, you must enable this option.

CaptureWithNavisworksOnly=1

![](./media/AdapterForNavisworks_image19.png)


### 8. Reduce Mesh Size
The AdapterForNavisworks provides an optional mesh reduction feature to reduce geometry complexity of Navisworks models when exporting to UdiTH.
```ini
ReduceMeshSize=1
```
- `1` = reduction enabled
- `0` or not set = no reduction (default behavior)

![](./media/AdapterForNavisworks_image20.png)

#### How It Works
- **Threshold:** Objects with **more than 3000 vertices** are considered for reduction.
- **Method:** Affected meshes are simplified using a suitable reduction algorithm (vertex/triangle reduction), preserving the object's visual appearance as much as possible (meaningful, low-loss reduction rather than plain decimation).
- Objects below the threshold remain unchanged.

#### Effects
| Area | Effect |
|---|---|
| UPVC file | Smaller file size |
| UdiTH model | Lower overall triangle count |
| Performance | Improved loading times and rendering performance in the viewer |

#### Recommendation
Enabling `ReduceMeshSize=1` is recommended especially for:
- Large plant models with many high-resolution meshes
- Performance-critical UdiTH deployments
- **Revit-based models**, which typically contain high-resolution meshes and therefore benefit significantly from this reduction

### 9. Excluding .vue files from an SVF session
When converting SVF sessions, you can exclude individual .vue files from the conversion process without having to recreate the SVF session file. This makes it easy to leave out any 3D content that is not relevant to the UDiTH model.

How it works: Place a plain text file named svfprojectname_ExcludeVues.txt into the same directory as your .vue files, where svfprojectname is the name of your SVF project file. Inside that text file, list the names of the .vue files you want to exclude, one entry per line.
Wildcard support: The file naming convention supports wildcards, giving you a flexible way to exclude multiple files at once. 
For example: *vendormodels*.vue
This excludes every .vue file whose name contains vendormodels, without having to list each file individually.

------------------------------------------------------------------------------------------------
## Command line mode

**AdapterForNavisworks** can also be used in command line mode.\
Use the following command to show the command line arguments:

**C:\\\>** **C:\\Program Files\\CAXperts\\Navisworks Adapter\\Navisworks Adapter.exe" /?**

Command line arguments:

![](./media/AdapterForNavisworks_image11.jpg)

**Example:**

`"C:\Program Files\CAXperts\Navisworks Adapter\Navisworks Adapter.exe" -input:C:\data\example.nwd -output:C:\UPVC\example.upvc`

## Log files

The software creates log files in the local `TEMP` directory, for example `C:\Temp\Navisworks Adapter\`. You can also open these files by double-clicking the CAXperts **AdapterForNavisworks** status bar:

<img src="./media/AdapterForNavisworks_image12.png" width="602" height="409">

---

# UDiTH Capture Station — AdapterForE3d

# AdapterForE3d

**AdapterForE3d** captures 3D graphics and attributes from AVEVA PDMS sessions, for example `*.rvm` and `*.att` files, and stores them in a `*.upvc` file. This file is then used as input for CAXperts **UDiTH Builder** to generate Universal Plant Viewer or 3D PDF sessions.

## Using AdapterForE3d

Start **AdapterForE3d** and select either one **input file** or multiple **input files**. PDMS `*.rvm` files are supported. Attribute files, such as `*.att` or `*.txt`, should be located in the same folder and should have the same name as the related `*.rvm` files.

<img src="./media/AdapterForE3d_image10.jpg" width="566" height="351">

**Output directory:** Defines the path to the `*.upvc` output file. This file contains all required information, such as graphics and attributes, needed to generate UDiTH or 3D PDF output using CAXperts **UDiTH Builder**.

If the `*.upvc` output file already exists, **AdapterForE3d** works in update mode and recaptures data only if the last modified date of the `*.rvm` file is newer than that of the `*.upvc` file.

## Aspects for PDMS models

In PDMS, it is possible to export 3D graphics together with additional aspects, for example insulation and obstruction. Because these aspects cannot be exported separately, but only together with the base graphic (`Simple Physical`), several files are produced that contain the base graphic plus the additional aspect.

AdapterForE3d can use an additional text file to assign this extra graphic directly to a corresponding aspect in the UPV model.

A template for this file is available at `C:\Program Files\CAXperts\AdapterForE3d\Templates\Aspect.txt`.

This file only needs to be present and adapted during conversion with the PDMS adapter in the directory where the `*.rvm` files are located.

You only need to specify the syntax of the files containing this additional aspect graphic.

This creates the additional graphics from the `*.rvm` files on separate aspect layers in UPV, where they can easily be switched on and off in the model.

<img src="./media/AdapterForE3d_image11.jpg" width="695" height="356">

`PDMS-Model.rvm` contains only the base graphic and is assigned to `Simple Physical`.

`PDMS-Model_Insulation.rvm` contains the base graphic and the insulation graphic and is assigned to `Insulation`.

`PDMS-Model_OBSTR.rvm` contains the base graphic and the obstruction graphic and is assigned to `Obstruction`.

## Additional `.ini` Parameters

Additional parameters can be defined in the `.ini` file.

The `.ini` file is usually located in `%AppData%\CAXperts\AdapterForE3d`.

### `unset` Attributes from E3D/PDMS

By default, all attribute values set to `unset` are skipped by AdapterForE3d.

However, these attributes can still be imported by adding an entry to the `.ini` file.

Import all attributes with the value `unset`:

```ini
[UnsetAttributes]
Unset=1
```

Import selected attributes with the value `unset`. Attribute names are separated by semicolons:

```ini
[UnsetAttributes]
Unset=mto; EQP_Status; Approval_date
```

<img src=".\media/AdapterForE3d_image14.jpg" width="506" height="209">

### Remove Duplicated Aspect Graphics

When exporting `*.rvm` files from PDMS, the aspects `Insulation` and `Obstruction` are always exported together with the basic solid graphic.

As a result, graphic elements may appear several times in the model. This can reduce viewing performance, especially in very large models. These duplicate graphics can be removed from the model afterwards by using an additional switch setting as a post-process step. This post-process may increase the conversion time.

```ini
[Settings]
RemoveDuplicatedAspectGraphics=1
```

<img src=".\media/AdapterForE3d_image15.jpg" width="386" height="239">

### Do Not Group Equipment Graphics

By default, the individual primitives of a piece of equipment are grouped together. This results in a smaller model size with fewer objects in UPV, which has a positive effect on navigation speed. However, this grouping can be disabled so that the smallest object units remain the primitives themselves.

```ini
[Settings]
DoNotGroupEquipment=1
```

<img src=".\media/AdapterForE3d_image16.jpg" width="386" height="239">

## Command line mode

**AdapterForE3d** can also be used in command line mode. Use the
following command to show the command line arguments:

```
C:\\\>"C:\\Program Files\CAXperts\\AdapterForE3d\\AdapterForE3d.exe" /?
```
Command line arguments:

<img src="./media/AdapterForE3d_image12.jpg" width="717" height="179">

`-input:C:\Example.rvm`

Occurrence: `AtLeastOnce`

Help text: Path to a PDMS `*.rvm` file or a directory containing multiple PDMS `*.rvm` files.

`-output:C:\Output\Example.upvc`

Occurrence: `AtMostOnce`

Help text: Path of the output file.

`-log`

Occurrence: `Arbitrary`

Help text: Path of the log file. If not defined, the log file is placed in `C:\Users\<user>\AppData\Local\Temp`.

## Log files

The software creates log files in the local `TEMP` directory, for example `C:\Temp\AdapterForE3d\`.

You can also open these files by double-clicking the CAXperts **AdapterForE3d** status bar:

<img src="./media/AdapterForE3d_image13.png" width="672" height="426">

---

# UDiTH Capture Station — AdapterForIfc

# AdapterForIfc

**AdapterForIfc** extracts graphics and attributes from *Industry Foundation Classes (IFC)* files and saves them in a `.upvc` file. This file is then used as input for the **Builder** to create UPV models.

Note: As of version 2026.3.4, the AdapterForIfc correctly decodes the IFC GlobalId to the internal UID.
If you would like to keep the previous behavior, please add the following entry to the INI file located in %appdata%\CAXperts\IFC Adapter\<username>.ini:

```
[IFC Adapter]
DecodeUidFromGlobalId=0
```


## Using AdapterForIfc

To start, launch **AdapterForIfc** and select one or more **Input Files**.

The following input types are supported:

- `.ifc` - Industry Foundation Classes (supports **IFC2X3** and **IFC4**)

<img src="./media/AdapterForIfc_AdapterForIfcUpload.png" width="600" height="">

**Output File** specifies the path for the **.upvc** file, which contains all necessary information, such as graphics and attributes, required to generate UPV models with **UDiTH Builder**.

## Command Line Mode

**AdapterForIfc** can also be started in command line mode. 

To get all parameters directly in a command window, just type:

`C:\Program Files\CAXperts\IFC Adapter>"IFC Adapter.exe" -?`

<img src="./media/AdapterForIfc_CMDAdapterForIfc.png" width="600">

Available parameters are:

-output:C:\\Output\\Example.upvc (Occurence: ExactlyOnce; Help Text: Path
to where the UPVC file will be created. File extension 'upvc' will be added, if not present.)

-input:C:\\Input\\Example.ifc (Occurence: AtLeastOnce; Help Text: Path to a IFC Adapter session file.)

-aspect:Simple physical (Occurence: Arbitrary; Help Text: The aspect to use.)

-config:c:\\config.ini (Occurence: Arbitrary; Help Text: The config file to use.)

-licensekey (Occurence: Arbitrary; Help Text: Activate license key.)

-log (Occurence: Arbitrary; Help Text: Log file path. If not defined, log file is placed in C:\\Users\\CAXperts\\AppData\Local\Temp)  

## Log Files

The software generates log files in the local TEMP directory (e.g., "C:\\Temp\\IFC Adapter\\"). Users can access these files by double-clicking on the status bar of CAXperts **AdapterForIfc**:

<img src=".\media/AdapterForIfc_image11.png" width="600" height="">

---

# UDiTH Capture Station — AdapterForLaserScans

# AdapterForLaserScans

**AdapterForLaserScans** captures point data from **E57** files and saves it as a **.upvc** file.

This file is then used as input for CAXperts **UDiTH** to generate visualisations.

## Supported Formats

### Laser scan

- E57

> Results may be better if the scanning position is included.

### Panorama

- TruViews
- LFM Panoramas

### Mesh

- OBJ (may include optional MTL and texture files)

## Overview

This section provides a brief overview of the main functions of **AdapterForLaserScans**.

For more detailed information, please refer to the dedicated chapters or [visit our help center](https://help.caxperts.com/).

<img src="./media/AdapterForLaserScans_image11.png" width="660" height="317">

### Operating Principle

**AdapterForLaserScans** converts point cloud data from a *laser scan file* into a *tessellated mesh*.

<img src="./media/AdapterForLaserScans_image8.png" width="500" height="">

It removes duplicate points and optimises cloud density.

<img src="./media/AdapterForLaserScans_image9.png" width="500" height="">

AdapterForLaserScans also integrates 3D textures into the laser scans, as illustrated below:

<img src="./media/AdapterForLaserScans_image10.png" width="500" height="">

### Interface

Select the input folder for AdapterForLaserScans and the required target folder.

<img src="./media/AdapterForLaserScans_image6.png" width="486" height="271">

## Settings

### Meshing Options

<img src="./media/AdapterForLaserScans_LaserScanAdapterSettings.png" width="500" height="">

### \%temp% Option

Given the large file sizes of laser scan data, you can specify the data path for temporary storage. Click **Browse** to select your preferred folder location.

><img src="./media/AdapterForLaserScans_Icon_Info.png" width="16" height="16"> The size of the laser scan files may affect the loading time of AdapterForLaserScans. Overlapping sections of each scan are compared.

### Configurations

**Create `defaultConfig.upv` with views** - This option creates default settings to save laser scan data (`.upv` files) with predefined views.

**Delete temporary files** - Enabling this option ensures that temporary files are deleted regularly to free up space.

<table>
<tr><th colspan="2"><strong>Image/Texture/Panorama Settings</strong></th></tr>
<tr><td>

Convert generated images to panoramas

</td><td>

When this option is enabled, you can choose a viewing point directly from the laser scan. If it is disabled, panoramas are not available through the red `360°` markers.

</td></tr>
<tr><td>

Fill images

</td><td>

Enabling this option improves the quality of the generated images.

*This does not work for TruViews and panoramas.*

</td></tr>
<tr><td>

Resolution

</td><td>

Adjusts the quality of the `360°` image. Move the slider to the left for lower quality (`4000 × 2000` pixels) and to the right for higher quality (`16000 × 8000` pixels).

</td></tr>
</table>

><img src="./media/AdapterForLaserScans_Icon_Info.png" width="16" height="16"> Using the highest resolution can reduce performance.

<table>
<tr><th colspan="2"><strong>OBJ Import</strong></th></tr>
<tr><td>

Aspect to use

</td><td>

<strong>.obj</strong> files can be imported. In **Settings** under **Aspects**, <strong>.obj</strong> files can be deactivated separately.

</td></tr>
</table>

## Command Line Mode

**AdapterForLaserScans** can also be started in command-line mode.

To display all parameters directly in a command window, enter:

`C:\Program Files\CAXperts\LaserscanAdapter>LaserscanAdapter.exe -?`

<img src="./media/AdapterForLaserScans_CMDAdapterForLaserScans.png" width="600" height="">

Available parameters are:

- `-input:C:\Input` (`Occurrence: ExactlyOnce`; Help Text: Path to a folder containing TruViews and laser scans)

- `-output:C:\Output\Example.upvc` (`Occurrence: ExactlyOnce`; Help Text: Path to where the UPVC file is created. File extension `upvc` is added if it is not already present.)

- `-licensekey` (`Occurrence: Arbitrary`; Help Text: Activates the licence key.)

- `-log` (`Occurrence: Arbitrary`; Help Text: Path to the log file. If not defined, the log file is placed in `C:\Users\CAXperts\AppData\Local\Temp`.)

## LaserscanAdapter

To locate this configuration file for editing, you can use the following path:

`%AppData%\CAXperts\LaserscanAdapter\<USERNAME>.ini`

><img src="./media/AdapterForLaserScans_Icon_Info.png" width="16" height="16"> Replace `<USERNAME>` with the actual user name when accessing the file.

Example:
```
[LaserscanAdapter]
DeleteTempFiles=1
TempFolder=%temp%
CreateDefaultConfig=1
ConvertImagesToPanorama=1
FillImages=1
ObjAspect=Simple Physical
TextureResolution=1024
MeshQuality=0
TruViewInput=C:\test
UPVCFile=C:\test.upvc
OutlierCheck=0
RemoveGhostObject=0
ImageType=CubeMapExtracted
sForceRgbMesh=0
[FrmMain]
WindowState=Normal
Top=142
Left=45
Height=330
Width=520
```

### INI Parameters

<table border="1">
    <thead>
        <tr>
            <th>Parameter</th>
            <th>Default Value</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>

<strong>BoxSize</strong>

</td>
            <td>

4 [meters]

</td>
            <td>

The mesh is split into boxes of `4 × 4 × 4` metres. If a box is too large or contains too many points, it can be split into smaller boxes. This speeds up calculations. Use the default size for indoor scans. For outdoor scans, such as landscapes or drone scans, use sizes greater than `10` metres.

</td>
        </tr>
        <tr>
            <td>

<strong>FurthestScanDistance</strong>

</td>
            <td>

30 [meters]

</td>
            <td>

Points farther than this distance from the scanner are ignored and removed. For example, with the default setting, a point `30.1` metres away is deleted.

</td>
        </tr>
        <tr>
            <td>

<strong>PointSamplingRate</strong>

</td>
            <td>

1

</td>
            <td>

This defines how often points are sampled from the point cloud. For example, if it is set to `3`, every third point is used for mesh calculations.

</td>
        </tr>
        <tr>
            <td>

<strong>ForceRgbMesh</strong>

</td>
            <td>

0

</td>
            <td>

When set to `1`, the system uses RGB information from the point cloud. This replaces the use of panorama textures.

</td>
        </tr>
        <tr>
            <td>

<strong>SimplifyMeshes</strong>

</td>
            <td>

1

</td>
            <td>

When set to `1`, the system reduces the number of triangles in the mesh after triangulation while maintaining good mesh quality.

</td>
        </tr>
        <tr>
            <td>

<strong>Simplification</strong>

</td>
            <td>

Between 0.001 and 0.000001 depending on the quality

</td>
            <td>

The higher this value, the more strongly the mesh is simplified. This results in fewer triangles.

</td>
        </tr>
        <tr>
            <td>

<strong>OctDownMaxNodeSize</strong>

</td>
            <td>

0.02 [meters]

</td>
            <td>

This determines the level of downsampling and affects the density of the point cloud used for meshing. A lower value means more points and more triangles. The recommended range is between `0.05` and `0.5` metres.

</td>
        </tr>
        <tr>
            <td>

<strong>RemoveGhostObject</strong>

</td>
            <td>

0

</td>
            <td>

When set to `1`, this removes parts of the point cloud that move and are not consistent across different scans.

</td>
        </tr>
        <tr>
            <td>

<strong>FumeCheck</strong>

</td>
            <td>

0

</td>
            <td>

When set to `1`, this removes fumes in the air. It works only if the `RemoveGhostObject` parameter is also set to `1`.

</td>
        </tr>
    </tbody>
</table>

---

# UDiTH Capture Station — AdapterForDocuments

# AdapterForDocuments

**AdapterForDocuments** captures 2D graphics and attributes from Intergraph SmartPlant SmartSketch and stores them in a `*.upvd` file. This file is then used as input for CAXperts **UDiTH Builder** to add 2D drawings to 3D sessions.

## Using AdapterForDocuments

## General Notes

**AdapterForDocuments** converts Smart 3D isometric and layout drawings, PDF files in general, and PDMS/E3D isometric drawings.
The resulting conversion format, `*.upvd`, can be integrated into a UDiTH model and linked intelligently.

### Notes for conversion with SmartSketch

SmartSketch may not always run completely reliably during the conversion process, which can result in licence seats not being returned properly to the licence server.

To avoid this, we recommend checking out a SmartSketch licence locally before running AdapterForDocuments. Please refer to the Intergraph documentation for details on how to do this.

![](./media/AdapterForDocuments_image10.png)

All Intergraph SmartSketch files are supported.

AdapterForDocuments works with SmartSketch version 2016 or later.

## Conversion of Smart 3D isometric drawings

The first tab, **S3D**, is used for processing Smart 3D isometrics and layout drawings. As with all tabs, this section is activated for processing only when the option is selected.
<img src="./media/AdapterForDocuments_image11.jpg" width="587" height="717">

This button enables processing of the isometric drawing into a `UPVD` file.

**AdapterForDocuments** starts **SmartSketch** and extracts the drawings into a `UPVD` file.

When you combine the `UPVD` file with the 3D model in **UDiTH Builder**, the resulting UPV session contains 3D objects linked to 2D isometric drawings, and vice versa:

<table>
<tr><td>

<img src="./media/AdapterForDocuments_image12.png" width="317" height="240">

</td><td>

<img src="./media/AdapterForDocuments_image13.png" width="317" height="240">

</td></tr>
</table>

## Conversion of PDF files

**AdapterForDocuments** can integrate all types of PDF file into a UDiTH model. When this section is activated, you only need to browse to a folder in the file system. The complete folder structure beneath that folder is then read and processed.

With an optional mapping file, all necessary links between the PDF and the relevant conditions in the model can be configured accordingly.

<img src="./media/AdapterForDocuments_image14.jpg" width="704" height="563">

An example for a mapping file is available in "C:\\Program Files
(x86)\\CAXperts\\AdapterForDocuments\\Templates\\PDF-Mapping.xlsx"

The synchronise button writes all links into the selected mapping-file.

## Conversion of PDMS/E3D isometric drawings

**AdapterForDocuments** can also convert PDMS/E3D isometrics. The converted isometrics include intelligent hotspots for all parts except straight pipes, so they have a direct link to the 3D model, just like S3D isometrics.

For this to work, the isometrics must be exported from PDMS as PDFs together with their hit files. This is an option within PDMS iso creation.

<img src="./media/AdapterForDocuments_image18.png" width="485" height="538">

## Additional settings

By default, AdapterForDocuments allows a single drawing name.

<img src="./media/AdapterForDocuments_image15.png" width="641" height="427">

## Command line mode

**AdapterForDocuments** can also be started in command line mode. To get all
parameters directly in a command window, just type:

\"C:\\Program Files
(x86)\\CAXperts\\AdapterForDocuments\\AdapterForDocuments.exe\" /?

<img src="./media/AdapterForDocuments_image16.png" width="589" height="125">

Available parameters are:

-output:D:\\path\\to\\file.upvd (Occurance: ExactlyOnce, Help Text: Path
to the output file)\
-sp3dfolder:C:\\drawings (Occurance: AtMostOnce, Help Text: Path to the
sp3d input folder)\
-pdffolder:C:\\pdfs (Occurance: AtMostOnce, Help Text: Path to the pdf input folder)\
-pdmse3dfolder:C:\pdmsE3d (Occurance: AtMostOnce, Help Text: Path to the pdmsE3d input folder)
-pdfmappingfile:C:\\pdfConfiguration\\mapping.xlsx (Occurance: AtMostOnce, Help Text: Path to the pdf mapping input file)\
-hierarchicalupdate (Occurance: AtMostOnce, Help Text: Determines
whether the dircetory structure of moved drawings should be updated)\
-onlycheckednodes (Occurance: AtMostOnce, Help Text: If selected, only
the checked nodes/drawings/pdfs will be updated/added. Otherwise all
nodes in all sub folder of the given folder will be considered).

Possible syntax for command-line:

\"C:\\Program Files
(x86)\\CAXperts\\AdapterForDocuments\\AdapterForDocuments.exe\"
-output:D:\\path\\file.upvd -sp3dfolder:C:\\drawings -pdffolder:C:\\pdfs -pdfmappingfile:C:\\pdfConfiguration\\mapping.xlsx

## Log files

The software creates log files in the local TEMP directory (e.g.
"C:\\Temp\\AdapterForDocuments\\". The user can also open these files by
double-clicking on the CAXperts **AdapterForDocuments** status bar:

<img src="./media/AdapterForDocuments_image17.png" width="682" height="394">

---

# UDiTH Capture Station — AdapterForSpid

# AdapterForSpid

## Introduction

The purpose of **AdapterForSpid** is to extract data used by Intergraph Smart® P&ID (SPID) into a local `UPVP` database for use with UDiTH.

## Graphical User Interface (GUI)

### Invoking **AdapterForSpid**

After starting, the following UI will appear:

<img src="./media/AdapterForSpid_image10.png" width="490" height="331">

### Loading a plant

The software automatically connects to the active plant. It is displayed in the field **Plant** and can be changed using the **Change active plant\...** button.

From the **Open plant structure** dialogue, you can select another plant from the active site. Plants from other sites can be accessed by using the **Site server\...** button.

<img src="./media/AdapterForSpid_image11.png" width="424" height="243">

Before loading the plant data, the **Output Access file** must be specified using the **Browse\...** button.

## Extracting data

Clicking the **Extract** button starts extraction from the selected plant. The following extraction options are available: **All**, **Selected drawings**, **Associated data**, and **Extract drawing files**.

### General extraction settings

#### All

The **All** option extracts all plant data without requiring further configuration.

#### Selected drawings

The **Selected drawings** option starts the extraction with a dialogue in which the required drawings can be selected.

A list of available drawings from the active project appears.
Select the required drawings in the dialogue and press **OK** to start the extraction.

<img src="./media/AdapterForSpid_image12.png" width="251" height="227">

<img src="./media/AdapterForSpid_image13.png" width="256" height="265">

In Connected Workshare projects, drawings that belong to the satellite are displayed in red so that the user is informed about their status.

#### Associated data

The **Associated data** option starts the extraction with a dialogue in which the required data can be defined by using SQL statements.

<img src="./media/AdapterForSpid_image14.png" width="685" height="313">

For example, the statement shown above extracts only pipe runs whose tags begin with `05`.

In the same way, it is possible to extract only pipe runs with `FluidSystem` set to `Water` or `Nominal Diameter` set to `100 mm`.

The same interface can also be used to search data, for example to check whether a certain sequence number has been used in the plant. The yellow bar can be used to enter a search string without performing the extraction:

<img src="./media/AdapterForSpid_image15.png" width="586" height="183">

#### Revision

The **Revision** option allows extraction of frozen data, but only for revisions that have an associated version. The dialogue allows you to select either the **Last Revision** data or the data for a specific revision:

![](./media/AdapterForSpid_image16.png)

### Settings related to the from/to calculation

The following options can be used when extracting the from/to information:

![](./media/AdapterForSpid_image17.png)

**Only equipment tag:** Reports only equipment tags, not nozzle tags.

**No branches:** Does not report branch connections.

**Use component description if no tag:** Reports the `Piping Component Description` attribute if the component tag is empty and if the component is an `EndComponent`.

In addition to these settings the following changes were implemented:

#### Tema heat exchangers

![](./media/AdapterForSpid_image18.png)

If the front and rear sections do not have their own item tags defined, the shell item tag is also extracted as from/to information for pipe runs connected to the front or rear section.

In the example above, all runs connected to the parts of the heat exchanger have `W-170` as from or to information.

#### Equipment with no tag

For equipment without a tag, it is possible to use a variable that tells the software which attribute to use as from/to information, for example `Name` or `Description`.

This is done by using the PID ReportAdapter `.ini` file:

`...\Program Files (x86)\CAXperts\IntelliPID Adapter\IntelliPID Adapter.ini`

```ini
[FROM_TO]
Equipment_NoTag=Name
```

## Additional SQL statements 

### Create additional SQL statements

SQL statements can be used to extend standard output tables with user-defined attributes or to perform other tasks, such as creating additional tables or queries. These statements can be defined using **Tools → Additional SQL statements**. They are executed automatically after the extraction process has been completed.

The additional SQL statements can be entered in the **Additional SQL statements to be processed after extraction** field. They should follow the format shown in the following examples:

`SQL1=ALTER TABLE Equipment ADD COLUMN [MyAttribute] VARCHAR(255)`

`SQL2=UPDATE [Equipment] SET [MyAttribute] = 'Test'`

![](./media/AdapterForSpid_image19.png)

The tables that can be used in the queries are those created during extraction. Additional tables, if required, must be copied before extraction into the `template.mdb` database, which can be found in the application folder.

### Use query builder

The included query builder can help you create SQL statements.
Create a new statement with the **Add** button and add one of the **Available SQL query templates** to it. Clicking **Add query**, or double-clicking a list item, inserts the query at the cursor position or replaces the selected text.

The **Available SQL query templates** look like this:

`UPDATE [%TABLE1%] SET [%TABLE1%].[%COLUMN1%]=`

When the template is used, the `%TABLE1%` and `%COLUMN1%` placeholders are filled with the table name and column name selected in the corresponding fields.

The `%TABLE1%` and `%COLUMN1%` fields are filled from the Access database defined for extraction output, which also includes the tables from `Template.mdb`. This means that the fields are filled only after extraction has been performed at least once.

If the **Show hidden tables** option is selected, all tables in the database can be chosen when building the query. If it is not selected, only the tables displayed in **UniversalReporter** are available, and tables created for internal use are hidden.

### Create new SQL query template

User-defined **SQL query templates** can be created. To add a new template, click the **Add** button and enter the template name.

<img src="./media/AdapterForSpid_image21.png" width="523" height="210">

Enter the SQL statement text and the placeholders for the **Tables and columns to be used by query** text boxes by using the `%TABLE1%` and `%COLUMN1%` buttons.

User-defined templates can be deleted or modified by using the **Remove** or **Edit** buttons.

> The SQL text boxes support syntax highlighting.

### Additional Settings
To ensure correct extraction for projects with a large number of P&IDs, it is recommended to set the parameter `BigPlant=1`.

For projects that include P&IDs with auxiliary graphics, you can use the parameter `auxiliary=1` to divide the full auxiliary graphics into individual graphical elements.

![](./media/AdapterForSpid_image48.png)

These parameters can be added to the `IntelliPID Adapter.ini` file located in the adapter installation folder.

![](./media/AdapterForSpid_image47.png)

## Configure attributes

### General functions

Select **Tools -\> Attribute configuration\...** to open the **Attribute
configuration** dialog.
Select **Tools -> Attribute configuration...** to open the **Attribute configuration** dialogue.

![](./media/AdapterForSpid_image22.png)

The following window appears.

![](./media/AdapterForSpid_image23.png)

In this dialogue, you can define the attributes to be extracted.

At present, attributes can be added for the following object types: `InlineComp_Prun`, `Eqp_Nozzle`, `PlantItems`, `Instruments`, `ItemNotes`, `OPCs`, and `Drawings`.

Additional attributes, which are not available in SmartPlant P&ID and are created during extraction, are displayed in blue, for example `End_2_ConnectedPiperunID` in the screenshot above.

The numbers displayed on the right side of the tables correspond to the number of available attributes on the left and the number of selected attributes on the right.

Expand the tables and select the required attributes. These can then be transferred to, or removed from, the **Attributes to be used** pane by using the `>` and `<` buttons.

### Filter
You can also search for specific attributes by using wildcard filters. In the following example, the search is for an attribute name containing `Flui` in the `InlineComp_Prun` table.

><img src="./media/AdapterForSpid_image1.png" width="675" height="435">

### Saving attribute configuration
The attribute configuration is saved in the folder `%APPDATA%\CAXperts\IntelliPID Adapter\` with the file name `PlantName.ini`. The attribute configuration can also be saved for backup or for batch-mode extraction by using:

![](./media/AdapterForSpid_image24.png)

## Attribute configuration specialties:

### ParentPlantGroup and UnitCode

For SPEM hierarchies such as `Plant\Area\Unit`, it is possible to extract both the `ParentPlantGroup` information and the `UnitCode` information:

![](./media/AdapterForSpid_image25.png)

This allows easy filtering, for example by area. The selection is available for all objects except `ItemNotes` and `OPCs`.

### PipingPoint attributes including ConnectedPiperunID

For piping connection points `1` to `4`, it is possible to extract attributes such as nominal diameter and description, as well as the `SP_ID` of the connected pipe run. This information can be used to create a report showing the pipe run connected to the second side of a pressure-reducing valve.

![](./media/AdapterForSpid_image26.png)

The result in **UniversalReporter** looks like this:

![](./media/AdapterForSpid_image27.png)

### CaseProcess SI values (also other properties SI values)

The SI values can also be selected for extraction.

![](./media/AdapterForSpid_image28.png)

### Filename and quantity

If `Filename` is selected at the `PlantItem` object level, the symbol name is also extracted, making it possible to see how often a symbol is placed on a drawing.

If `Quantity` is selected, which is available on all object levels, each extracted object is assigned the quantity `1`. In **UniversalReporter**, it is then possible to create `Sum_of_quantity` in order to identify the total number of valves, for example.

![](./media/AdapterForSpid_image29.png)

### Connectivity attributes for the relations "Equipment to PipeRun" and "Instruments to PipeRuns or Equipments"

Although they are not visible in the attribute configuration interface, the following two attributes are extracted:

-   **Nozzle_ConnectedPipeRunID** for the `Eqp_Nozzle` table

-   **Instrument_ConnectedItemID** for the `Instruments` table

These attributes can be used to create reports in which the pipe runs connected to an item of equipment are listed, starting at the equipment object level, or to identify instrument connections, which may be to pipe runs or equipment nozzles.

![](./media/AdapterForSpid_image30.png)

![](./media/AdapterForSpid_image31.png)

**Example for the Equipment_PipeRun Relation**:

![](./media/AdapterForSpid_image32.png)

**Example for the Instrument Connection Report**:

There are multiple ways to connect instruments to nozzles or pipe runs.

The following connections, related to piping components placed between the connected pipe run or connected equipment nozzle and the instrument, have been modified in PID ReportAdapter:

**A**: The instrument is connected to a valve placed in a branch of the main pipe run. The branch is returned as the connected object.

**B**: The instrument is connected to a valve that is connected to a nozzle, where the pipe run type is `Conn to process/Supply`. In this case, the nozzle is returned as the connected object.

**C**: The instrument is connected to a valve that is connected to a nozzle through a `ZeroLength` pipe run. In this case, the nozzle is returned as the connected object.

**D**: The instrument is connected to a valve placed in a pipe run that is itself connected to a nozzle. The pipe run has a special line type. In this case, the nozzle is returned as the connected object. In the example below, the line type is `Components only`.

The definition of the pipe run type can be set in `..\PID ReportAdapter.ini` with the following entry:

```ini
[Instr_Conn]
Nozzle_Pruntype = "Components only"
```

![](./media/AdapterForSpid_image33.png)

![](./media/AdapterForSpid_image34.png)

Details for C and D:

![](./media/AdapterForSpid_image35.png)

Result:

![](./media/AdapterForSpid_image36.png)

### Disconnected piperuns having the same ItemTag

If `NotConnectedRuns` is selected at the `InlineComp_Prun` object level under `Others`, the number of disconnected pipe runs with the same tag is calculated for the extracted dataset.

![](./media/AdapterForSpid_image37.png)

![](./media/AdapterForSpid_image38.png)

Within a drawing, the function checks whether the runs are graphically connected. Across drawings, it checks whether the pipe runs are connected via `OPCs`. Any result greater than `0` indicates duplicate pipe run tags that are not graphically connected.

Warning whenever the database is opened.

The extraction of `NotConnectedRuns` can take some time for larger datasets.

### Identify number of LineNumberLabels placed on a piperun

For all pipe runs, except instrument runs and zero-length pipe runs, if the attribute `NumberofLabels` is selected and the label name or names are defined in the plant-specific `.ini` file, the number of labels placed on each pipe run is calculated during extraction.

In the `.ini` file, located at `.UserProfile\AppData\Roaming\CAXperts\AdapterForSpid\`

`<PlantName>.ini`, the relative path for the line label or labels to be checked must be added:

![](./media/AdapterForSpid_image39.png)

To define more than one label for checking, separate the labels with a semicolon, as shown in the following example:

`Piping=\Piping\Labels - Piping Segments\Line Number.sym;\Piping\Labels - Piping Segments\Line Number_old.sym`

All results different from `1` should be checked. `0` means that no label is placed, while values greater than `1` mean that more than one label is placed on the same run.

![](./media/AdapterForSpid_image40.png)

### Identify the "Parent" relationship between components

By using the attribute `SP_PartOfId`, which is available in the `PlantItem` area for all components, it is possible to extract relationships between an actuator and a valve, an equipment component and its equipment, and similar relationships.

![](./media/AdapterForSpid_image41.png)

![](./media/AdapterForSpid_image42.png)

### SymbolName and coordinates

The `Symbol` table has been added to the extraction interface so that fields from the symbol table can be selected, including the calculated field `Name`, which refers to the name of the symbol, for example `Gate Valve`.

![](./media/AdapterForSpid_image43.png)

### MultiRep for equipment

A new attribute called `MultiRep` has been added to the `Equipment_Nozzle` attribute configuration in the `Others` area:

![](./media/AdapterForSpid_image44.png)

It is calculated during extraction and shows the value `True` for MultiRepresentation equipment and `False` for other equipment.

## Command line mode

**AdapterForSpid** can also be used in command-line mode in order to extract all data from a plant to a `UPVP` file automatically from a batch script.

Use the following command to display the available command-line arguments:

![](./media/AdapterForSpid_image45.png)

Command line arguments:

<img src="./media/AdapterForSpid_image46.png" width="629" height="207">

**Examples:**

`"C:\Program Files (x86)\CAXperts\IntelliPID Adapter\IntelliPID Adapter.exe" -upvp:"d:\temp\PlantA.upvp"`

`"C:\Program Files (x86)\CAXperts\IntelliPID Adapter\IntelliPID Adapter.exe" -upvp:"d:\temp\PlantA.upvp" -site:"d:\Site1\smartplantv4.ini" --plant:PlantA --parentplant:PlantA --config:"d:\PlantA.ini"`

`"C:\Program Files (x86)\CAXperts\IntelliPID Adapter\IntelliPID Adapter.exe" -upvp:d:\temp\1.upvp -pbs:Refining Area\Recovery Unit;\Refining Area\Distillation Unit\a_*`

> This saves all drawings with names starting with `a_` and located in `Refining Area\Recovery Unit` or `Refining Area\Distillation Unit`.

`"C:\Program Files (x86)\CAXperts\IntelliPID Adapter\IntelliPID Adapter.exe" -upvp:d:\temp\1.upvp -unitcode:03`

> This saves all drawings located below the unit with the unit code `03`.

The parameters `-pbs` and `-unitcode` must be used separately, that is, either `-pbs` or `-unitcode`.

The attribute configuration `.ini` file, used in the command line with the `-config` parameter, can be created by using the menu **File - Save settings to file**.

The options **Extract Drawing Files** and **Extract From-To relations for InlineComp_Prun** from interactive mode are saved in the file

`%APPDATA%\CAXperts\IntelliPID Adapter\config.ini`

for example `C:\Documents and Settings\UserName\Application Data\CAXperts\IntelliPID Adapter\config.ini`.

In command-line mode, these two options are read from `config.ini` and used for extraction.

---

# UDiTH Capture Station — AdapterForPdfPid

# AdapterForPdfPid

## Introduction

The purpose of **AdapterForPdfPid** is to extract data from different PDF files into a local `UPVP` database.
**AdapterForPdfPid** scans PDFs and creates a semi-intelligent P&ID database file for later use in Builder and IntelliPID.

The program recognises **text labels** and sorts them into user-defined categories such as equipment, pipe runs, instruments, and off-page connectors.
 
The user-defined categories for label recognition must be stored in an Excel worksheet.

The name of this file must be **config.xlsx**.

An example of `config.xlsx` can be found in:

> `\Program Files\CAXperts\PdfPidAdapter\Templates`

The names of sheet 1 and sheet 2 can be changed. It is important only that both sheets remain in the correct order.

Sheet 1 = `Patterns`

<img src="./media/AdapterForPdfPid_image6.png" width="600" height="300">

Sheet 2 = `Settings`

<img src="./media/AdapterForPdfPid_image7.png" width="600" height="300">

## Graphical User Interface

After starting **AdapterForPdfPid**, the following window appears:

<img src="./media/AdapterForPdfPid_image1.png" width="463" height="226">

After selecting the input directory and defining the output file, the capture process can begin.

The Excel file named `config.xlsx` must be in the same folder as the imported PDFs.

Start the process by clicking the **Capture** button.

### DocType

AdapterForPdfPid can also be used to convert other PDF drawings, such as equipment arrangement plans or isometric drawings, in order to create semi-intelligent drawings and map them to the 3D model. The document type can be defined in `config.xlsx` in sheet 2. In the example shown here, that sheet is called `Settings`.

For `DocType`, any name can be chosen. Please note that `IntelliPID` is a protected term. Therefore, the IntelliPID sketching module is available only for this document type. If no `DocType` is set in the `Settings` sheet, the documents in this folder are treated automatically as IntelliPID. If no `Settings` sheet exists in `config.xlsx`, the documents in this folder are also treated automatically as IntelliPID.

The PDF file containing the specific drawing type should be a **single-page** document. Otherwise, it is treated as `DocType "IntelliPID"`.

### IgnoreZone

By defining an ignore zone, you can exclude text recognition by AdapterForPdfPid. Text labels within the defined zone are ignored. This can be useful, for example, when excluding text recognition in the title block of a drawing.

<img src="./media/AdapterForPdfPid_image5.png" width="600" height="350">

### Example PDF P&ID

The following images show the process that **AdapterForPdfPid** follows to generate semi-intelligent P&IDs.

Example input PDF with tank, pumps, valves, lines, and additional information:

<img src="./media/AdapterForPdfPid_image2.png" width="600" height="450">

Extract from the resulting `config.xlsx` file. This Excel file contains the relevant data to be collected from several input PDFs.
The information is based on regular expressions (`regex`).

<img src="./media/AdapterForPdfPid_image3_new.png" width="700" height="430">

### Builder

When viewed in [**IntelliPID**](https://help.caxperts.com/UDiTH%20App/IntelliPID%20Module), the result shows the full graphical representation and also indicates that the main text labels are recognised as intelligent objects.
In the following example, the semi-intelligent information is marked yellow.

<img src="./media/AdapterForPdfPid_image4_new.png" width="700" height="430">

For detailed information about 3D/P&ID object linking by using mapping files, see the [Builder documentation](https://help.caxperts.com/UDiTH%20Capture%20Station/Builder#pid-3d-mapping). Additional attributes can also be loaded by using the Builder plugin [ExcelImportPlugin](https://help.caxperts.com/UDiTH%20Capture%20Station/ExcelImportPlugin).

---

# UDiTH Capture Station — SDK

# SDK

The **SDK for UDiTH Builder** allows you to create custom plugins that are executed by **UDiTH Builder**.
With these plugins, you can create new attributes and graphics, or read, update, and delete existing ones.

## Working with SDK for UDiTH Builder

### Creating plugins

To develop a custom plugin, you first need to reference the `UDiTHBuilderPlugin.dll` file in your plugin project. Your custom plugin class must implement the `IPluginVersion1` interface and its `BeforeExport` method. This method is then called by UDiTH Builder.

The `PreExportHook` object that is passed as a parameter to `BeforeExport` is the single entry point. It contains various accessors and methods that allow you to create or modify attributes and graphics dynamically. See the API documentation below for details.

## Prerequisites for debugging

If you want to debug your custom plugin, you must adjust both the **Start Action** and the **Build Events** of your project:

### Start action

Enter the path to your `UDiTH Builder` executable file, for example `%ProgramFiles%\CAXperts\UDiTH Builder\UDiTH Builder.exe`.

### Build events

Under **Post-Build Events**, enter the following:

`COPY /Y "$(TargetPath)" "%AppData%\CAXperts\UDiTH Builder\Plugins"`

This is required because UniversalPlantViewer Builder executes every DLL found in the `%AppData%\CAXperts\UDiTH Builder\Plugins` directory.

### Dependencies

If your plugin requires third-party dependencies or other DLL files, you must place them inside a `libs` folder next to your custom plugin DLL files.

![](./media/Builder2_image32.png)

### Running your plugins

Once you have deployed your custom plugin DLLs to the folder in which the SDK looks for plugins, `%AppData%\CAXperts\UDiTH Builder\Plugins`, you can select the plugins to be executed in **Settings -> Plugins**:

![](./media/Builder2_image28.png)

### API

The various accessors described below can be accessed through the `PreExportHook` object that is passed as a parameter to the `BeforeExport` method. The `BeforeExport` method is the single entry point for a custom plugin. A plugin class must implement the `IPluginVersion1` interface and its `BeforeExport` method. This method is then called by the SDK in UDiTH Builder.

#### BuilderContext

The builder context is used for writing log information to the UDiTH Builder output file.

#### void Log
(string message)

Writes a message to the UDiTH Builder output file. Requires a `string` message parameter and returns `void`.

#### void LogError
(string message, Exception e)

Writes a message and an exception to the UDiTH Builder output file. Requires a `string` message and an `Exception` object as parameters and returns `void`.

#### void CancelExecution()

Throws a new `CanceledExecutionException`.

#### void CancelExecution
(string message)

Throws a new `CanceledExecutionException` with the specified message. Requires a `string` message parameter.

#### BuilderElementAccessor

This accessor contains methods for accessing elements and session objects.

#### IEnumerable\<ISession\> Sessions { get; }

This property returns the available session objects as an `IEnumerable<ISession>`.

#### IEnumerable\<IElement\> IterateElements()

Iterates over all elements and returns an `IEnumerable<IElement>`.

#### IElement GetElement
(Guid uid)

Accesses a single element by its `uid`. Requires the `uid` as a parameter and returns either the matching element or `null` if no element is found.

#### IElement CreateElement
(Guid uid, ISession session)

Creates a new builder element. Requires a `uid` and a session object as parameters and returns the newly created element.

#### void DeleteElement
(Guid uid, ISession session)

Completely deletes an existing element, including all its attributes and graphics. Requires the `uid` and the session object of the element as parameters and returns `void`.

#### BuilderAttributeAccessor

Contains various methods for interacting with attributes.

#### IEnumerable\<string\> IterateAttributes()

Iterates over all attribute keys and returns an `IEnumerable<string>`.

#### IEnumerable\<IElement\> FindElementsByAttributes
(string conditionQuery)

Finds all elements that match a given condition. Requires the query condition string as a parameter and returns an `IEnumerable<IElement>`. The query condition has the form `Name=Value`. Several conditions can be combined with an `&` character.

#### void RenameAttribute
(string originalName, string newName)

Changes an attribute key for all elements. Requires the old and new attribute keys as parameters and returns `void`.

#### void DeleteAttribute
(string name)

Completely removes an attribute. Requires the attribute name as a parameter and returns `void`.

#### IDictionary\<string, string\> GetAttributes
(IElement element, HashSet\<string\> attributes = null)

Gets all attributes for a specific element. Requires the element as a parameter and returns a dictionary of attributes. Optionally, a `HashSet<string>` containing the attribute keys to extract can be passed as a second parameter.

#### void AddAttribute
(IElement element, string name, string value)

Adds a new attribute to an existing element. Requires the element, the attribute name, and the attribute value. Returns `void`.

#### void UpdateAttribute
(IElement element, string name, string value)

Updates an existing attribute of an element. Requires the element, the attribute name, and the attribute value. Returns `void`.

#### void RenameAttribute
(IElement element, string oldName, string newName)

Renames an existing attribute of an element. Requires the element, the old attribute name, and the new attribute name. Returns `void`.

#### void DeleteAttribute
(IElement element, string name)

Removes an existing attribute from an element. Requires the element and the attribute name as parameters. Returns `void`.

#### BuilderGraphicAccessor

Contains various methods for getting, creating, and modifying graphics.

#### ISessionTransformation GetSessionTransformation
(ISession session)

Returns the transformation information for the corresponding session object. Requires the session object as an input parameter.

#### IEnumerable\<string\> GetAspects()

Accesses all available aspects and returns an `IEnumerable<string>`.

#### void CreateAspect
(string aspectName)

Creates a new aspect. Requires the new aspect name as a parameter and returns `void`.

#### void DeleteAspect
(string aspectName)

Deletes an existing aspect. Requires the aspect name as input and returns `void`.

#### IEnumerable\<IGraphicElement\> GetGraphics
(IElement element)

Gets all graphic objects of an element. Requires the element as an input parameter and returns an `IEnumerable<IGraphicElement>`.

#### IGraphicElement CreateGraphic
(IElement element, string aspectName, IEnumerable\<string\> primitives)

Creates a new graphic. Requires the element, the aspect name, and the primitives. Returns the newly created graphic element.

#### IEnumerable\<string\> GetPrimitives
(IGraphicElement graphicElement)

Accesses the primitives of a graphic element and returns an `IEnumerable<string>`.

#### void SetPrimitives
(IGraphicElement graphicElement, IEnumerable\<string\> primitives)

Sets the primitives for a graphic element. Requires the graphic element and the new primitives as parameters. Returns `void`.

#### void SetColor
(IGraphicElement graphicElement, Color colour)

Changes the colour of a graphic element. Requires the graphic element and the new colour as parameters. Returns `void`.

#### BuilderLinkAccessor

Used to attach links to elements.

#### ILink CreateLink
(string url, Color colour, string name = null, LinkType type = LinkType.Default)

Creates a new link. Use the returned identifier to register elements to the link.

#### void AddRelation
(ILink link, IElement element)

Registers an element to the link.

#### BuilderDocumentAccessor

Contains methods for registering and linking documents with elements.

#### IDocument CreatePdf
(byte\[\] fileContent, string name, string treePath)

Registers a new PDF file, which is then written to the general output location.

#### void AddRelation
(IDocument document, IElement element)

Links an element to a created document.

#### BuilderIntelliPidElementAccessor

This accessor contains methods for accessing IntelliPid elements and session objects.

#### IEnumerable\<ISession\> Sessions { get; }

This property returns the available session objects as an `IEnumerable<ISession>`.

#### IEnumerable\<IIntelliPidElement\> IterateElements()

Iterates over all elements and returns an `IEnumerable<IIntelliPidElement>`.

#### IIntelliPidElement GetElement
(Guid uid)

Accesses a single element by its `uid`. Requires the `uid` as a parameter and returns either the matching element or `null` if no element is found.

#### BuilderIntelliPidAttributeAccessor

Contains various methods for interacting with IntelliPid attributes.

#### IEnumerable\<string\> IterateAttributes()

Iterates over all IntelliPid attribute keys and returns an `IEnumerable<string>`.

#### IEnumerable\<IIntelliPidElement\> FindElementsByAttributes
(string conditionQuery)

Finds all IntelliPid elements that match a given condition. Requires the query condition string as a parameter and returns an `IEnumerable<IIntelliPidElement>`. The query condition has the form `Name=Value`. Several conditions can be combined with an `&` character.

#### void RenameAttribute
(string originalName, string newName)

Changes an IntelliPid attribute key for all IntelliPid elements. Requires the old and new attribute keys as parameters and returns `void`.

#### void DeleteAttribute
(string name)

Completely removes an IntelliPid attribute. Requires the attribute name as a parameter and returns `void`.

#### IDictionary\<string, string\> GetAttributes
(IIntelliPidElement element, HashSet\<string\> attributes = null)

Gets all IntelliPid attributes for a specific IntelliPid element. Requires the IntelliPid element as a parameter and returns a dictionary of attributes. Optionally, a `HashSet<string>` containing the attribute keys to extract can be passed as a second parameter.

#### void AddAttribute
(IIntelliPidElement element, string name, string value)

Adds a new attribute to an existing IntelliPid element. Requires the IntelliPid element, the attribute name, and the value. Returns `void`.

#### void UpdateAttribute
(IIntelliPidElement element, string name, string value)

Updates an existing attribute of an IntelliPid element. Requires the IntelliPid element, the attribute name, and the value. Returns `void`.

#### void RenameAttribute
(IIntelliPidElement element, string oldName, string newName)

Renames an existing attribute of an IntelliPid element. Requires the IntelliPid element, the old attribute name, and the new attribute name. Returns `void`.

#### void DeleteAttribute
(IIntelliPidElement element, string name)

Removes an existing attribute from an IntelliPid element. Requires the element and the attribute name as parameters. Returns `void`.

---

# UDiTH Capture Station — SDK - UniversalPlantViewerBuilderPlugin Namespace

# UniversalPlantViewerBuilderPlugin Namespace

UniversalPlantViewerBuilderPlugin.Version1 Namespace

**\[Missing \<summary\> documentation for
\"N:UniversalPlantViewerBuilderPlugin.Version1\"\]**

### Classes

<table>
<tr><th>Class</th><th>Description</th></tr>
<tr><td>

[CanceledExecutionException](#canceledexecutionexception-class)

</td><td>

-

</td></tr>
</table>     

### Structures

<table>
<tr><th>Structure</th><th>Description</th></tr>
<tr><td>

[Color](#color-structure)

</td><td>

-

</td></tr>
<tr><td>

[Vector3D](#vector3d-structure)

</td><td>

-

</td></tr>
</table>

### Interfaces

<table>
<tr><th>Interface</th><th>Description</th></tr>
<tr><td>

[IBuilderAttributeAccessor](#ibuilderattributeaccessor-interface)

</td><td>

Exposes methods to modify the internal attributes Notes: AttributeNames are treated case insensitive.

</td></tr>
<tr><td>

[IBuilderContext](#ibuildercontext-interface)

</td><td>

Exposes general builder methods.

</td></tr>
<tr><td>

[IBuilderDocumentAccessor](#ibuilderdocumentaccessor-interface)

</td><td>

Exposes methods to manipulate documents shown in the tree (currently Docs only, 2D/PID might follow in the future).

</td></tr>
<tr><td>

[IBuilderElementAccessor](#ibuilderelementaccessor-interface)

</td><td>

-

</td></tr>
<tr><td>

[IBuilderElementAccessor](#ibuilderelementaccessor-interface)

</td><td>

-

</td></tr>
<tr><td>

[IBuilderGraphicAccessor](#ibuildergraphicaccessor-interface)

</td><td>

-

</td></tr>
<tr><td>

[IBuilderIntelliPidAttributeAccessor](#ibuilderintellipidattributeaccessor-interface)

</td><td>

Exposes methods to modify the internal attributes Notes: AttributeNames are treated case insensitive

</td></tr>
<tr><td>

[IBuilderIntelliPidElementAccessor](#ibuilderintellipidelementaccessor-interface)

</td><td>

-

</td></tr>
<tr><td>

[IBuilderLinkAccessor](#ibuilderlinkaccessor-interface)

</td><td>

-

</td></tr>
<tr><td>

[IDocument](#idocument-interface)

</td><td>

-

</td></tr>
<tr><td>

[IElement](#ielement-interface)

</td><td>

-

</td></tr>
<tr><td>

[IGraphicElement](#igraphicelement-interface)

</td><td>

-

</td></tr>
<tr><td>

[IIntelliPidElement](#iintellipidelement-interface)

</td><td>

-

</td></tr>
<tr><td>

[ILink](#ilink-interface)

</td><td>

-

</td></tr>
<tr><td>

[IPluginVersion1](#ipluginversion1-interface)

</td><td>

-

</td></tr>
<tr><td>

[IPreExportHook](#ipreexporthook-interface)

</td><td>

-

</td></tr>
<tr><td>

[ISession](#isession-interface)

</td><td>

-

</td></tr>
<tr><td>

[ISessionTransformation](#isessiontransformation-interface)

</td><td>

-

</td></tr>
</table>

### Enumerations

<table>
<tr><th>Enumeration</th><th>Description</th></tr>
<tr><td>

[LinkType](#linktype-enumeration)

</td><td>

-

</td></tr>
</table>

## CanceledExecutionException Class

**\[Missing \<summary\> documentation for
\"T:UniversalPlantViewerBuilderPlugin.Version1.CanceledExecutionException\"\]**

### Inheritance Hierarchy

[System.Object](http://msdn2.microsoft.com/en-us/library/e5kfa45b)  [System.Exception](http://msdn2.microsoft.com/en-us/library/c18k6c59)    UniversalPlantViewerBuilderPlugin.Version1.CanceledExecutionException

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -----------------------------------------------------
  public class CanceledExecutionException : Exception

### Constructors

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[CanceledExecutionException()](#canceledexecutionexception-constructor-1)

</td><td>

Initializes a new instance of the **CanceledExecutionException** class.

</td></tr>
<tr><td>

[CanceledExecutionException(String)](#canceledexecutionexception-constructor-string)

</td><td>

Initializes a new instance of the **CanceledExecutionException** class

</td></tr>
<tr><td>

[CanceledExecutionException(String, Exception)](#canceledexecutionexception-constructor-string-exception)

</td><td>

Initializes a new instance of the **CanceledExecutionException** class

</td></tr>
</table>       

### See Also

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## CanceledExecutionException Constructor 

### Overload List

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[CanceledExecutionException()](#canceledexecutionexception-constructor-1)

</td><td>

Initializes a new instance of the [CanceledExecutionException](#canceledexecutionexception-class) class

</td></tr>
<tr><td>

[CanceledExecutionException(String)](#canceledexecutionexception-constructor-string)

</td><td>

Initializes a new instance of the [CanceledExecutionException](#canceledexecutionexception-class) class

</td></tr>
<tr><td>

[CanceledExecutionException(String, Exception)](#canceledexecutionexception-constructor-string-exception)

</td><td>

Initializes a new instance of the [CanceledExecutionException](#canceledexecutionexception-class) class

</td></tr>
</table>

### See Also

[CanceledExecutionException Class](#canceledexecutionexception-class)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## CanceledExecutionException Constructor 

Initializes a new instance of the
[CanceledExecutionException](#canceledexecutionexception-class) class

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -------------------------------------
  public CanceledExecutionException()

### See Also

[CanceledExecutionException Class](#canceledexecutionexception-class)

[CanceledExecutionException
Overload](#canceledexecutionexception-constructor)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## CanceledExecutionException Constructor (String)

Initializes a new instance of the
[CanceledExecutionException](#canceledexecutionexception-class) class

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  -------------------------------------
  C\#
  -------------------------------------
  public CanceledExecutionException(\
  string *message* )
  -------------------------------------

#### Parameters

*message*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

**\[Missing \<param name=\"message\"/\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.CanceledExecutionException.\#ctor(System.String)\"\]**

### See Also

[CanceledExecutionException Class](#canceledexecutionexception-class)

[CanceledExecutionException
Overload](#canceledexecutionexception-constructor)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## CanceledExecutionException Constructor (String, Exception)

Initializes a new instance of the
[CanceledExecutionException](#canceledexecutionexception-class) class

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  -------------------------------------
  C\#
  -------------------------------------
  public CanceledExecutionException(\
  string *message*,\
  Exception *inner*\
  )

  -------------------------------------

#### Parameters

*message*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

**\[Missing \<param name=\"message\"/\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.CanceledExecutionException.\#ctor(System.String,System.Exception)\"\]**

*inner*

Type:
[System.Exception](http://msdn2.microsoft.com/en-us/library/c18k6c59)

**\[Missing \<param name=\"inner\"/\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.CanceledExecutionException.\#ctor(System.String,System.Exception)\"\]**

### See Also

[CanceledExecutionException Class](#canceledexecutionexception-class)

[CanceledExecutionException
Overload](#canceledexecutionexception-constructor)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## Colour Structure

**\[Missing \<summary\> documentation for
\"T:UniversalPlantViewerBuilderPlugin.Version1.Color\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  ---------------------
  public struct Color

The **Colour** type exposes the following members.

### Properties

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[A](#color.a-property)

</td><td>

-

</td></tr>
<tr><td>

[B](#color.b-property)

</td><td>

-

</td></tr>
<tr><td>

[G](#color.g-property)

</td><td>

-

</td></tr>
<tr><td>

[R](#color.r-property)

</td><td>

-

</td></tr>
</table>

### See Also

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## Color.Color Properties

The [Color](#color-structure) type exposes the following members.

### Properties

table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[A](#color.a-property)

</td><td>

-

</td></tr>
<tr><td>

[B](#color.b-property)

</td><td>

-

</td></tr>
<tr><td>

[G](#color.g-property)

</td><td>

-

</td></tr>
<tr><td>

[R](#color.r-property)

</td><td>

-

</td></tr>
</table>

### See Also

[Color Structure](#color-structure)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## Color.A Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.Color.A\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -----------------------------
  public byte A { get; set; }

#### Property Value

Type: [Byte](http://msdn2.microsoft.com/en-us/library/yyb1w04y)

### See Also

[Color Structure](#color-structure)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## Color.B Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.Color.B\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -----------------------------
  public byte B { get; set; }

#### Property Value

Type: [Byte](http://msdn2.microsoft.com/en-us/library/yyb1w04y)

## See Also

[Color Structure](#color-structure)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## Color.G Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.Color.G\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -----------------------------
  public byte G { get; set; }

#### Property Value

Type: [Byte](http://msdn2.microsoft.com/en-us/library/yyb1w04y)

### See Also

[Color Structure](#color-structure)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## Color.R Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.Color.R\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -----------------------------
  public byte R { get; set; }

#### Property Value

Type: [Byte](http://msdn2.microsoft.com/en-us/library/yyb1w04y)

### See Also

[Color Structure](#color-structure)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderAttributeAccessor Interface

Exposes methods to modify the internal attributes Notes: AttributeNames
are treated case insensitive

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  --------------------------------------------
  public interface IBuilderAttributeAccessor

The **IBuilderAttributeAccessor** type exposes the following members.

### Methods

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[AddAttribute](#ibuilderattributeaccessor.addattribute-method)

</td><td>

Add a new attribute to an element.

</td></tr>
<tr><td>

[DeleteAttribute(String)](#ibuilderattributeaccessor.deleteattribute-method-string)

</td><td>

Completely remove an attribute.

</td></tr>
<tr><td>

[DeleteAttribute(IElement, String)](#ibuilderattributeaccessor.deleteattribute-method-ielement-string)

</td><td>

Remove attribute from element.

</td></tr>
<tr><td>

[DeleteElement](#ibuilderattributeaccessor.deleteelement-method)

</td><td>

**Obsolete.** Fully delete an element (mesh and attributes)

</td></tr>
<tr><td>

[FindElementsByAttributes](#ibuilderattributeaccessor.findelementsbyattributes-method) 

</td><td>

Find all elements by condition

</td></tr>
<tr><td>

[GetAttributes](#ibuilderattributeaccessor.getattributes-method)

</td><td>

Extract all attribute values of an element.

</td></tr>
<tr><td>

[GetElement](#ibuilderattributeaccessor.getelement-method)

</td><td>

**Obsolete.** Access a single element

</td></tr>
<tr><td>

[IterateAttributes](#ibuilderattributeaccessor.iterateattributes-method)

</td><td>

Iterate over all attribute keys.

</td></tr>
<tr><td>

[IterateElements](#ibuilderattributeaccessor.iterateelements-method)

</td><td>

**Obsolete.** Iterate over all elements

</td></tr>
<tr><td>

[RenameAttribute(String, String)](#ibuilderattributeaccessor.renameattribute-method-string-string)

</td><td>

Change an attribute key for all elements.

</td></tr>
<tr><td>

[RenameAttribute(IElement, String, String)](#ibuilderattributeaccessor.renameattribute-method-ielement-string-string)

</td><td>

Rename existing attribute in element.

</td></tr>
<tr><td>

[UpdateAttribute](#ibuilderattributeaccessor.updateattribute-method)

</td><td>

Update existing attribute in element.

</td></tr>
</table>

### See Also

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderAttributeAccessor.IBuilderAttributeAccessor Methods

The [IBuilderAttributeAccessor](#ibuilderattributeaccessor-interface) 
type exposes the following members.

### Methods

<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[Add Attribute](#ibuilderattributeaccessor.addattribute-method)

</td><td>

Add a new attribute to an element.

</td></tr>
<tr><td>

[DeleteAttribute(String)](#ibuilderattributeaccessor.deleteattribute-method-string)

</td><td>

Completely remove an attribute

</td></tr>
<tr><td>

[DeleteAttribute(IElement, String)](#ibuilderattributeaccessor.deleteattribute-method-ielement-string)

</td><td>

 Remove attribute from element.

</td></tr>
<tr><td>

[DeleteElement](#ibuilderattributeaccessor.deleteelement-method)

</td><td>

**Obsolete.** Fully delete an element (mesh and attributes)

</td></tr>
<tr><td>

[FindElementsByAttributes](#ibuilderattributeaccessor.findelementsbyattributes-method)

</td><td>

Find all elements by condition.

</td></tr>
<tr><td>

[GetAttributes](#ibuilderattributeacessor.getattributes-method)

</td><td>

Extract all attribute values of an element

</td></tr>
<tr><td>

[GetElement](#ibuilderattributeaccessor.getelement-method)

</td><td>

**Obsolete.** Access a single element.

</td></tr>
<tr><td>

[IterateAttributes](#ibuilderattributeaccessor.iterateattributes-method)

</td><td>

Iterate over all attribute keys.

</td></tr>
<tr><td>

[IterateElements](#ibuilderattributeaccessor.iterateelements-method)

</td><td>

**Obsolete.** Iterate over all elements.

</td></tr>
<tr><td>

[RenameAttribute(String, String)](#ibuilderattributeaccessor.renameattribute-method-string-string)

</td><td>

Change an attribute key for all elements.

</td></tr>
<tr><td>

[RenameAttribute(IElement, String, String)](#ibuilderattributeaccessor.renameattribute-mthod-ielement-string-string)

</td><td>

Rename existing attribute in element.

</td></tr>
<tr><td>

[UpdateAttribute](#ibuilderattributeaccessor.updateattribute-method)

</td><td>

Update existing attribute in element.

</td></tr>
</table>

### See Also

[IBuilderAttributeAccessor
Interface](#ibuilderattributeaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderAttributeAccessor.AddAttribute Method 

Add a new attribute to an element

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  ----------------------
  C\#
  ----------------------
  void AddAttribute(\
  IElement *element*,\
  string *name*,\
  string *value*\
  )

  ----------------------

#### Parameters

*element*

Type:
[UniversalPlantViewerBuilderPlugin.Version1.IElement](#ielement-interface)

The element

*name*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The name of the new attribute

*value*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The value of the new attribute

### See Also

[IBuilderAttributeAccessor
Interface](#ibuilderattributeaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderAttributeAccessor.DeleteAttribute Method 

### Overload List

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[DeleteAttribute(String)](#ibuilderattributeaccessor.deleteattribute-method-string)

</td><td>

Completely remove an attribute

</td></tr>
<tr><td>

[DeleteAttribute(IElement, String)](#ibuilderattributeaccessor.deleteattribute-method-ielement-string)

</td><td>

Remove attribute from element

</td></tr>
</table>

## See Also

[IBuilderAttributeAccessor
Interface](#ibuilderattributeaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderAttributeAccessor.DeleteAttribute Method (String)

Completely remove an attribute

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  ------------------------
  C\#
  ------------------------
  void DeleteAttribute(\
  string *name*\
  )

  ------------------------

#### Parameters

*name*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The name of the attribute to remove

### See Also

[IBuilderAttributeAccessor
Interface](#ibuilderattributeaccessor-interface)

[DeleteAttribute
Overload](#ibuilderattributeaccessor.deleteattribute-method)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderAttributeAccessor.DeleteAttribute Method (IElement, String)

Remove attribute from element

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  ------------------------
  C\#
  ------------------------
  void DeleteAttribute(\
  IElement *element*,\
  string *name*\
  )

  ------------------------

#### Parameters

*element*

Type:
[UniversalPlantViewerBuilderPlugin.Version1.IElement](#ielement-interface)

The element

*name*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The name of the attribute to be removed

### See Also

[IBuilderAttributeAccessor
Interface](#ibuilderattributeaccessor-interface)

[DeleteAttribute
Overload](#ibuilderattributeaccessor.deleteattribute-method)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderAttributeAccessor.DeleteElement Method 

> This API is now obsolete.

Fully delete an element (mesh and attributes)

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  ------------------------
  C\#
  ------------------------
  \[ObsoleteAttribute\]\
  void DeleteElement(\
  Guid *uid*\
  )

  ------------------------

#### Parameters

*uid*

Type: [System.Guid](http://msdn2.microsoft.com/en-us/library/cey1zx63)

The element\'s uid

### See Also

[IBuilderAttributeAccessor
Interface](#ibuilderattributeaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderAttributeAccessor.FindElementsByAttributes Method 

Find all elements by condition

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  ----------------------------------------------------
  C\#
  ----------------------------------------------------
  IEnumerable\<IElement\> FindElementsByAttributes(\
  string *conditionQuery*\
  )

  ----------------------------------------------------

#### Parameters

*conditionQuery*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The filter condition (e.g. \"Name=Value\")

#### Return Value

Type:
[IEnumerable](http://msdn2.microsoft.com/en-us/library/9eekhta0)([IElement](#ielement-interface))**\[Missing
\<returns\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderAttributeAccessor.FindElementsByAttributes(System.String)\"\]**

### See Also

[IBuilderAttributeAccessor
Interface](#ibuilderattributeaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderAttributeAccessor.GetAttributes Method 

Extract all attribute values of an element

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  -----------------------------------------------
  C\#
  -----------------------------------------------
  IDictionary\<string, string\> GetAttributes(\
  IElement *element*,\
  HashSet\<string\> *attributes* = null\
  )

  -----------------------------------------------

#### Parameters

*element*

Type:
[UniversalPlantViewerBuilderPlugin.Version1.IElement](#ielement-interface)

The element

*attributes* (Optional)

Type:
[System.Collections.Generic.HashSet](http://msdn2.microsoft.com/en-us/library/bb359438)([String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf))

The attributes to extract (can be null in order to extract all attributes)

#### Return Value

Type:
[IDictionary](http://msdn2.microsoft.com/en-us/library/s4ys34ea)([String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf),
[String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf))**\[Missing
\<returns\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderAttributeAccessor.GetAttributes(UniversalPlantViewerBuilderPlugin.Version1.IElement,System.Collections.Generic.HashSet{System.String})\"\]**

### See Also

[IBuilderAttributeAccessor
Interface](#ibuilderattributeaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderAttributeAccessor.GetElement Method 

> This API is now obsolete.

Access a single element

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  ------------------------
  C\#
  ------------------------
  \[ObsoleteAttribute\]\
  IElement GetElement(\
  Guid *uid*\
  )

  ------------------------

#### Parameters

*uid*

Type: [System.Guid](http://msdn2.microsoft.com/en-us/library/cey1zx63)

The element\'s uid

#### Return Value

Type: [IElement](#ielement-interface)**\[Missing \<returns\>
documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderAttributeAccessor.GetElement(System.Guid)\"\]**

### See Also

[IBuilderAttributeAccessor
Interface](#ibuilderattributeaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderAttributeAccessor.IterateAttributes Method 

Iterate over all attribute keys

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -------------------------------------------
  IEnumerable\<string\> IterateAttributes()

#### Return Value

Type:
[IEnumerable](http://msdn2.microsoft.com/en-us/library/9eekhta0)([String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf))**\[Missing
\<returns\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderAttributeAccessor.IterateAttributes\"\]**

### See Also

[IBuilderAttributeAccessor
Interface](#ibuilderattributeaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderAttributeAccessor.IterateElements Method 

> This API is now obsolete.

Iterate over all elements

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  -------------------------------------------
  C\#
  -------------------------------------------
  \[ObsoleteAttribute\]\
  IEnumerable\<IElement\> IterateElements()

  -------------------------------------------

#### Return Value

Type:
[IEnumerable](http://msdn2.microsoft.com/en-us/library/9eekhta0)([IElement](#ielement-interface))**\[Missing
\<returns\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderAttributeAccessor.IterateElements\"\]**

### See Also

[IBuilderAttributeAccessor
Interface](#ibuilderattributeaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderAttributeAccessor.RenameAttribute Method 

### Overload List

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[RenameAttribute(String, String)](#ibuilderattributeaccessor.renameattribute-method-string-string)

</td><td>

Change an attribute key for all elements.

</td></tr>
<tr><td>

[RenameAttribute(IElement, String, String)](#ibuilderattributeaccessor.renameattribute-method-ielement-string-string)

</td><td>

Rename existing attribute in element.

</td></tr>
</table>

### See Also

[IBuilderAttributeAccessor
Interface](#ibuilderattributeaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderAttributeAccessor.RenameAttribute Method (String, String)

Change an attribute key for all elements

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  -------------------------
  C\#
  -------------------------
  void RenameAttribute(\
  string *originalName*,\
  string *newName*\
  )

  -------------------------

#### Parameters

*originalName*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The old attribute key

*newName*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The new attribute key

### See Also

[IBuilderAttributeAccessor
Interface](#ibuilderattributeaccessor-interface)

[RenameAttribute
Overload](#ibuilderattributeaccessor.renameattribute-method)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderAttributeAccessor.RenameAttribute Method (IElement, String, String)

Rename existing attribute in element

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  ------------------------
  C\#
  ------------------------
  void RenameAttribute(\
  IElement *element*,\
  string *oldName*,\
  string *newName*\
  )

  ------------------------

#### Parameters

*element*

Type:
[UniversalPlantViewerBuilderPlugin.Version1.IElement](#ielement-interface)

The element

*oldName*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The old name of the attribute to be renamed

*newName*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The new name of the attribute

### See Also

[IBuilderAttributeAccessor
Interface](#ibuilderattributeaccessor-interface)

[RenameAttribute
Overload](#ibuilderattributeaccessor.renameattribute-method)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderAttributeAccessor.UpdateAttribute Method 

Update existing attribute in element

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  ------------------------
  C\#
  ------------------------
  void UpdateAttribute(\
  IElement *element*,\
  string *name*,\
  string *value*\
  )

  ------------------------

#### Parameters

*element*

Type:
[UniversalPlantViewerBuilderPlugin.Version1.IElement](#ielement-interface)

The element

*name*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The name of the existing attribute

*value*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The new value

### See Also

[IBuilderAttributeAccessor
Interface](#ibuilderattributeaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderContext Interface

Exposes general builder methods

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  ----------------------------------
  public interface IBuilderContext

The **IBuilderContext** type exposes the following members.

### Methods

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[CancelExecution()](#ibuildercontext.cancelexecution-method-1)

</td><td>

Cancel execution

</td></tr>
<tr><td>

[CancelExecution(String)](#ibuildercontext.cancelexecution-method-string)

</td><td>

Cancel execution and print to the UPVB output file

</td></tr>
<tr><td>

[Log](#ibuildercontext.log-method)

</td><td>

Print to the UPVB output file

</td></tr>
<tr><td>

[LogError](#ibuildercontext.logerror-method)

</td><td>

Print an exception stacktrace to the UPVB output file

</td></tr>
</table>

### See Also

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderContext.IBuilderContext Methods

The [IBuilderContext](#ibuildercontext-interface) type exposes the
following members.

### Methods

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[CancelExecution()](#ibuildercontext.cancelexecution-method-1)

</td><td>

Cancel execution

</td></tr>
<tr><td>

[CancelExecution(String)](#ibuildercontext.cancelexecution-method-string)

</td><td>

Cancel execution and print to the UPVB output file

</td></tr>
<tr><td>

[Log](#ibuildercontext.log-method)

</td><td>

Print to the UPVB output file

</td></tr>
<tr><td>

[LogError](#ibuildercontext.logerror-method)

</td><td>

Print an exception stacktrace to the UPVB output file

</td></tr>
</table>

### See Also

[IBuilderContext Interface](#ibuildercontext-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderContext.CancelExecution Method 

### Overload List

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[CancelExecution()](#ibuildercontext.cancelexecution-method-1)

</td><td>

Cancel execution

</td></tr>
<tr><td>

[CancelExecution(String)](#ibuildercontext.cancelexecution-method-string)

</td><td>

Cancel execution and print to the UPVB output file

</td></tr>
</table>   

### See Also

[IBuilderContext Interface](#ibuildercontext-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderContext.CancelExecution Method 

Cancel execution

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  ------------------------
  void CancelExecution()

### See Also

[IBuilderContext Interface](#ibuildercontext-interface)

[CancelExecution Overload](#ibuildercontext.cancelexecution-method)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderContext.CancelExecution Method (String)

Cancel execution and print to the UPVB output file

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  ------------------------
  C\#
  ------------------------
  void CancelExecution(\
  string *message*\
  )

  ------------------------

#### Parameters

*message*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The message

### See Also

[IBuilderContext Interface](#ibuildercontext-interface)

[CancelExecution Overload](#ibuildercontext.cancelexecution-method)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderContext.Log Method 

Print to the UPVB output file

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  -------------------
  C\#
  -------------------
  void Log(\
  string *message*\
  )

  -------------------

#### Parameters

*message*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The message

### See Also

[IBuilderContext Interface](#ibuildercontext-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderContext.LogError Method 

Print an exception stacktrace to the UPVB output file

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  --------------------
  C\#
  --------------------
  void LogError(\
  string *message*,\
  Exception *e*\
  )

  --------------------

#### Parameters

*message*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The message

*e*

Type:
[System.Exception](http://msdn2.microsoft.com/en-us/library/c18k6c59)

The exception object

### See Also

[IBuilderContext Interface](#ibuildercontext-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderDocumentAccessor Interface

Exposes methods to manipulate documents shown in the tree (currently
Docs only, 2D/PID might follow in the future)

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -------------------------------------------
  public interface IBuilderDocumentAccessor

The **IBuilderDocumentAccessor** type exposes the following members.

### Methods

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[AddRelation](#ibuilderdocumentaccessor.addrelation-method)

</td><td>

Link an element to a created document

</td></tr>
<tr><td>

[CreatePdf](#ibuilderdocumentaccessor.createpdf-method)

</td><td>

Register a new pdf file which will be written to the general output location

</td></tr>
</table>

### See Also

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderDocumentAccessor.IBuilderDocumentAccessor Methods

The [IBuilderDocumentAccessor](#ibuilderdocumentaccessor-interface) type
exposes the following members.

### Methods

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[AddRelation](#ibuilderdocumentaccessor.addrelation-method)

</td><td>

Link an element to a created document

</td></tr>
<tr><td>

[CreatePdf](#ibuilderdocumentaccessor.createpdf-method)

</td><td>

Register a new pdf file which will be written to the general output location

</td></tr>
</table>        

### See Also

[IBuilderDocumentAccessor
Interface](#ibuilderdocumentaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderDocumentAccessor.AddRelation Method 

Link an element to a created document

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  ------------------------
  C\#
  ------------------------
  void AddRelation(\
  IDocument *document*,\
  IElement *element*\
  )

  ------------------------

#### Parameters

*document*

Type:
[UniversalPlantViewerBuilderPlugin.Version1.IDocument](#idocument-interface)

**\[Missing \<param name=\"document\"/\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderDocumentAccessor.AddRelation(UniversalPlantViewerBuilderPlugin.Version1.IDocument,UniversalPlantViewerBuilderPlugin.Version1.IElement)\"\]**

*element*

Type:
[UniversalPlantViewerBuilderPlugin.Version1.IElement](#ielement-interface)

**\[Missing \<param name=\"element\"/\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderDocumentAccessor.AddRelation(UniversalPlantViewerBuilderPlugin.Version1.IDocument,UniversalPlantViewerBuilderPlugin.Version1.IElement)\"\]**

### See Also

[IBuilderDocumentAccessor
Interface](#ibuilderdocumentaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderDocumentAccessor.CreatePdf Method 

Register a new pdf file which will be written to the general output
location

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  --------------------------
  C\#
  --------------------------
  IDocument CreatePdf(\
  byte\[\] *fileContent*,\
  string *name*,\
  string *treePath*\
  )

  --------------------------

#### Parameters

*fileContent*

Type:
[System.Byte](http://msdn2.microsoft.com/en-us/library/yyb1w04y)\[\]

**\[Missing \<param name=\"fileContent\"/\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderDocumentAccessor.CreatePdf(System.Byte\[\],System.String,System.String)\"\]**

*name*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

**\[Missing \<param name=\"name\"/\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderDocumentAccessor.CreatePdf(System.Byte\[\],System.String,System.String)\"\]**

*treePath*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

f.e. \"SDK/PDfs\" - when null or empty will not show the pdf in the tree

#### Return Value

Type: [IDocument](#idocument-interface)**\[Missing \<returns\>
documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderDocumentAccessor.CreatePdf(System.Byte\[\],System.String,System.String)\"\]**

### See Also

[IBuilderDocumentAccessor
Interface](#ibuilderdocumentaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderElementAccessor Interface

**\[Missing \<summary\> documentation for
\"T:UniversalPlantViewerBuilderPlugin.Version1.IBuilderElementAccessor\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  ------------------------------------------
  public interface IBuilderElementAccessor

The **IBuilderElementAccessor** type exposes the following members.

### Properties

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[Sessions](#ibuilderelementaccessor.sessions-property)

</td><td>

-

</td></tr>
</table>

### Methods

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[CreateElement](#ibuilderelementaccessor.createelement-method)

</td><td>

Create a new builder element

</td></tr>
<tr><td>

[DeleteElement](#ibuilderelementaccessor.deleteelement-method)

</td><td>

Fully delete an element (mesh and attributes)

</td></tr>
<tr><td>

[GetElement](#ibuilderelementaccessor.getelement-method)

</td><td>

Access a single element

</td></tr>
<tr><td>

[IterateElements](#ibuilderelementaccessor.iterateelements-method)

</td><td>

Iterate over all elements

</td></tr>
</table>

### See Also

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderElementAccessor.IBuilderElementAccessor Properties

The [IBuilderElementAccessor](#ibuilderelementaccessor-interface) type
exposes the following members.

### Properties

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[Sessions](#ibuilderelementaccessor.sessions-property)

</td><td>

</td></tr>
</table>

### See Also

[IBuilderElementAccessor Interface](#ibuilderelementaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderElementAccessor.Sessions Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.IBuilderElementAccessor.Sessions\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -------------------------------------------
  IEnumerable\<ISession\> Sessions { get; }

#### Property Value

Type:
[IEnumerable](http://msdn2.microsoft.com/en-us/library/9eekhta0)([ISession](#isession-interface))

### See Also

[IBuilderElementAccessor Interface](#ibuilderelementaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderElementAccessor.IBuilderElementAccessor Methods

The [IBuilderElementAccessor](#ibuilderelementaccessor-interface) type
exposes the following members.

### Methods

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[CreateElement](#ibuilderelementaccessor.createelement-method)

</td><td>

Create a new builder element

</td></tr>
<tr><td>

[DeleteElement](#ibuilderelementaccessor.deleteelement-method)

</td><td>

Fully delete an element (mesh and attributes)

</td></tr>
<tr><td>

[GetElement](#ibuilderelementaccessor.getelement-method)

</td><td>

Access a single element

</td></tr>
<tr><td>

[IterateElements](#ibuilderelementaccessor.iterateelements-method)

</td><td>

Iterate over all elements

</td></tr>
</table>

### See Also

[IBuilderElementAccessor Interface](#ibuilderelementaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderElementAccessor.CreateElement Method 

Create a new builder element

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  --------------------------
  C\#
  --------------------------
  IElement CreateElement(\
  Guid *uid*,\
  ISession *session*\
  )

  --------------------------

#### Parameters

*uid*

Type: [System.Guid](http://msdn2.microsoft.com/en-us/library/cey1zx63)

The uid of the element

*session*

Type:
[UniversalPlantViewerBuilderPlugin.Version1.ISession](#isession-interface)

The session object of the element

#### Return Value

Type: [IElement](#ielement-interface)**\[Missing \<returns\>
documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderElementAccessor.CreateElement(System.Guid,UniversalPlantViewerBuilderPlugin.Version1.ISession)\"\]**

### See Also

[IBuilderElementAccessor Interface](#ibuilderelementaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderElementAccessor.DeleteElement Method 

Fully delete an element (mesh and attributes)

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  ----------------------
  C\#
  ----------------------
  void DeleteElement(\
  Guid *uid*\
  )

  ----------------------

#### Parameters

*uid*

Type: [System.Guid](http://msdn2.microsoft.com/en-us/library/cey1zx63)

The uid of the element

### See Also

[IBuilderElementAccessor Interface](#ibuilderelementaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderElementAccessor.GetElement Method 

Access a single element

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  -----------------------
  C\#
  -----------------------
  IElement GetElement(\
  Guid *uid*\
  )

  -----------------------

#### Parameters

*uid*

Type: [System.Guid](http://msdn2.microsoft.com/en-us/library/cey1zx63)

The uid of the element

#### Return Value

Type: [IElement](#ielement-interface)**\[Missing \<returns\>
documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderElementAccessor.GetElement(System.Guid)\"\]**

### See Also

[IBuilderElementAccessor Interface](#ibuilderelementaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderElementAccessor.IterateElements Method 

Iterate over all elements

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -------------------------------------------
  IEnumerable\<IElement\> IterateElements()

#### Return Value

Type:
[IEnumerable](http://msdn2.microsoft.com/en-us/library/9eekhta0)([IElement](#ielement-interface))**\[Missing
\<returns\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderElementAccessor.IterateElements\"\]**

### See Also

[IBuilderElementAccessor Interface](#ibuilderelementaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

# IBuilderGraphicAccessor Interface

**\[Missing \<summary\> documentation for
\"T:UniversalPlantViewerBuilderPlugin.Version1.IBuilderGraphicAccessor\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  ------------------------------------------
  public interface IBuilderGraphicAccessor

The **IBuilderGraphicAccessor** type exposes the following members.

### Methods

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[CreateAspect](#ibuildergraphicaccessor.createaspect-method)

</td><td>

Create a new aspect

</td></tr>
<tr><td>

[CreateGraphic](#ibuildergraphicaccessor.creategraphic-method)

</td><td>

Create a new graphic object

</td></tr>
<tr><td>

[DeleteAspect](#ibuildergraphicaccessor.deleteaspect-method)

</td><td>

Delete an existing aspect

</td></tr>
<tr><td>

[GetAspects](#ibuildergraphicaccessor.getaspects-method)

</td><td>

Get all aspects

</td></tr>
<tr><td>

[GetGraphics](#ibuildergraphicaccessor.getgraphics-method)

</td><td>

Get all graphic objects of an element

</td></tr>
<tr><td>

[GetPrimitives](#ibuildergraphicaccessor.getprimitives-method)

</td><td>

Get primitives of a graphic element

</td></tr>
<tr><td>

[GetSessionTransformation](#ibuildergraphicaccessor.getsessiontransformation-method)

</td><td>

Get transformation object for the corresponding session

</td></tr>
<tr><td>

[SetColor](#ibuildergraphicaccessor.setcolor-method)

</td><td>

Set the color of a graphic element

</td></tr>
<tr><td>

[SetPrimitives](#ibuildergraphicaccessor.setprimitives-method)

</td><td>

Set primitives of a graphic element

</td></tr>
</table>

### See Also

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderGraphicAccessor.IBuilderGraphicAccessor Methods

The [IBuilderGraphicAccessor](#ibuildergraphicaccessor-interface) type
exposes the following members.

### Methods

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[CreateAspect](#ibuildergraphicaccessor.createaspect-method)

</td><td>

Create a new aspect

</td></tr>
<tr><td>

[CreateGraphic](#ibuildergraphicaccessor.creategraphic-method)

</td><td>

Create a new graphic object

</td></tr>
<tr><td>

[DeleteAspect](#ibuildergraphicaccessor.deleteaspect-method)

</td><td>

Delete an existing aspect

</td></tr>
<tr><td>

[GetAspects](#ibuildergraphicaccessor.getaspects-method)

</td><td>

 Get all aspects

</td></tr>
<tr><td>

[GetGraphics](#ibuildergraphicaccessor.getgraphics-method)

</td><td>

Get all graphic objects of an element

</td></tr>
<tr><td>

[GetPrimitives](#ibuildergraphicaccessor.getprimitives-method)

</td><td>

Get primitives of a graphic element

</td></tr>
<tr><td>

[GetSessionTransformation](#ibuildergraphicaccessor.getsessiontransformation-method)

</td><td>

Get transformation object for the corresponding session

</td></tr>
<tr><td>

[SetColor](#ibuildergraphicaccessor.setcolor-method)

</td><td>

Set the color of a graphic element

</td></tr>
<tr><td>

[SetPrimitives](#ibuildergraphicaccessor.setprimitives-method)

</td><td>

Set primitives of a graphic element

</td></tr>
</table>

### See Also

[IBuilderGraphicAccessor Interface](#ibuildergraphicaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderGraphicAccessor.CreateAspect Method 

Create a new aspect

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  ----------------------
  C\#
  ----------------------
  void CreateAspect(\
  string *aspectName*\
  )

  ----------------------

#### Parameters

*aspectName*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The name of the new aspect

### See Also

[IBuilderGraphicAccessor Interface](#ibuildergraphicaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderGraphicAccessor.CreateGraphic Method 

Create a new graphic object

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  -------------------------------------
  C\#
  -------------------------------------
  IGraphicElement CreateGraphic(\
  IElement *element*,\
  string *aspectName*,\
  IEnumerable\<string\> *primitives*\
  )

  -------------------------------------

#### Parameters

*element*

Type:
[UniversalPlantViewerBuilderPlugin.Version1.IElement](#ielement-interface)

The element

*aspectName*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The aspect name

*primitives*

Type:
[System.Collections.Generic.IEnumerable](http://msdn2.microsoft.com/en-us/library/9eekhta0)([String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf))

The primitives of the new graphic object

#### Return Value

Type: [IGraphicElement](#igraphicelement-interface)**\[Missing
\<returns\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderGraphicAccessor.CreateGraphic(UniversalPlantViewerBuilderPlugin.Version1.IElement,System.String,System.Collections.Generic.IEnumerable{System.String})\"\]**

### See Also

[IBuilderGraphicAccessor Interface](#ibuildergraphicaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderGraphicAccessor.DeleteAspect Method 

Delete an existing aspect

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  ----------------------
  C\#
  ----------------------
  void DeleteAspect(\
  string *aspectName*\
  )

  ----------------------

#### Parameters

*aspectName*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The name of the aspect

### See Also

[IBuilderGraphicAccessor Interface](#ibuildergraphicaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderGraphicAccessor.GetAspects Method 

Get all aspects

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  ------------------------------------
  IEnumerable\<string\> GetAspects()

#### Return Value

Type:
[IEnumerable](http://msdn2.microsoft.com/en-us/library/9eekhta0)([String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf))**\[Missing
\<returns\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderGraphicAccessor.GetAspects\"\]**

### See Also

[IBuilderGraphicAccessor Interface](#ibuildergraphicaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderGraphicAccessor.GetGraphics Method 

Get all graphic objects of an element

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  ----------------------------------------------
  C\#
  ----------------------------------------------
  IEnumerable\<IGraphicElement\> GetGraphics(\
  IElement *element*\
  )

  ----------------------------------------------

#### Parameters

*element*

Type:
[UniversalPlantViewerBuilderPlugin.Version1.IElement](#ielement-interface)

The element

#### Return Value

Type:
[IEnumerable](http://msdn2.microsoft.com/en-us/library/9eekhta0)([IGraphicElement](#igraphicelement-interface))**\[Missing
\<returns\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderGraphicAccessor.GetGraphics(UniversalPlantViewerBuilderPlugin.Version1.IElement)\"\]**

### See Also

[IBuilderGraphicAccessor Interface](#ibuildergraphicaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderGraphicAccessor.GetPrimitives Method 

Get primitives of a graphic element

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  ---------------------------------------
  C\#
  ---------------------------------------
  IEnumerable\<string\> GetPrimitives(\
  IGraphicElement *graphicElement*\
  )

  ---------------------------------------

#### Parameters

*graphicElement*

Type:
[UniversalPlantViewerBuilderPlugin.Version1.IGraphicElement](#igraphicelement-interface)

The graphic element

#### Return Value

Type:
[IEnumerable](http://msdn2.microsoft.com/en-us/library/9eekhta0)([String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf))**\[Missing
\<returns\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderGraphicAccessor.GetPrimitives(UniversalPlantViewerBuilderPlugin.Version1.IGraphicElement)\"\]**

### See Also

[IBuilderGraphicAccessor Interface](#ibuildergraphicaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderGraphicAccessor.GetSessionTransformation Method 

Get transformation object for the corresponding session

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  ---------------------------------------------------
  C\#
  ---------------------------------------------------
  ISessionTransformation GetSessionTransformation(\
  ISession *session*\
  )

  ---------------------------------------------------

#### Parameters

*session*

Type:
[UniversalPlantViewerBuilderPlugin.Version1.ISession](#isession-interface)

The session

#### Return Value

Type:
[ISessionTransformation](#isessiontransformation-interface)**\[Missing
\<returns\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderGraphicAccessor.GetSessionTransformation(UniversalPlantViewerBuilderPlugin.Version1.ISession)\"\]**

### See Also

[IBuilderGraphicAccessor Interface](#ibuildergraphicaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderGraphicAccessor.SetColor Method 

Set the color of a graphic element

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  ------------------------------------
  C\#
  ------------------------------------
  void SetColor(\
  IGraphicElement *graphicElement*,\
  Color *color*\
  )

  ------------------------------------

#### Parameters

*graphicElement*

Type:
[UniversalPlantViewerBuilderPlugin.Version1.IGraphicElement](#igraphicelement-interface)

The graphic element

*color*

Type:
[UniversalPlantViewerBuilderPlugin.Version1.Color](#color-structure)

The new color of the graphic element

### See Also

[IBuilderGraphicAccessor Interface](#ibuildergraphicaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderGraphicAccessor.SetPrimitives Method 

Set primitives of a graphic element

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  -------------------------------------
  C\#
  -------------------------------------
  void SetPrimitives(\
  IGraphicElement *graphicElement*,\
  IEnumerable\<string\> *primitives*\
  )

  -------------------------------------

#### Parameters

*graphicElement*

Type:
[UniversalPlantViewerBuilderPlugin.Version1.IGraphicElement](#igraphicelement-interface)

The graphic element

*primitives*

Type:
[System.Collections.Generic.IEnumerable](http://msdn2.microsoft.com/en-us/library/9eekhta0)([String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf))

The new primitives of the graphic element

### See Also

[IBuilderGraphicAccessor Interface](#ibuildergraphicaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderIntelliPidAttributeAccessor Interface

Exposes methods to modify the internal attributes Notes: AttributeNames
are treated case insensitive

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  ------------------------------------------------------
  public interface IBuilderIntelliPidAttributeAccessor

The **IBuilderIntelliPidAttributeAccessor** type exposes the following
members.

### Methods

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[AddAttribute](#ibuilderintellipidattributeaccessor.addattribute-method)

</td><td>

Add a new attribute to an element

</td></tr>
<tr><td>

[DeleteAttribute(String)](#ibuilderintellipidattributeaccessor.deleteattribute-method-string)

</td><td>

Completely remove an attribute

</td></tr>
<tr><td>

[DeleteAttribute(IIntelliPidElement, String)](#ibuilderintellipidattributeaccessor.deleteattribute-method-iintellipidelement-string)

</td><td>

Remove attribute from element

</td></tr>
<tr><td>

[FindElementsByAttributes](#ibuilderintellipidattributeaccessor.findelementsbyattributes-method)

</td><td>

Find all elements by condition

</td></tr>
<tr><td>

[GetAttributes](#ibuilderintellipidattributeaccessor.getattributes-method)

</td><td>

 Extract all attribute values of an element

</td></tr>
<tr><td>

[IterateAttributes](#ibuilderintellipidattributeaccessor.iterateattributes-method)

</td><td>

Iterate over all attribute keys

</td></tr>
<tr><td>

[RenameAttribute(String, String)](#ibuilderintellipidattributeaccessor.renameattribute-method-string-string)

</td><td>

Change an attribute key for all elements

</td></tr>
<tr><td>

[RenameAttribute(IIntelliPidElement, String, String)](#ibuilderintellipidattributeaccessor.renameattribute-method-iintellipidelement-string-string)

</td><td>

Rename existing attribute in element

</td></tr>
<tr><td>

[UpdateAttribute](#ibuilderintellipidattributeaccessor.updateattribute-method)

</td><td>

Update existing attribute in element

</td></tr>
</table>

### See Also

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderIntelliPidAttributeAccessor.IBuilderIntelliPidAttributeAccessor Methods

The
[IBuilderIntelliPidAttributeAccessor](#ibuilderintellipidattributeaccessor-interface)
type exposes the following members.

### Methods

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[AddAttribute](#ibuilderintellipidattributeaccessor.addattribute-method)

</td><td>

Add a new attribute to an element

</td></tr>
<tr><td>

[DeleteAttribute(String)](#ibuilderintellipidattributeaccessor.deleteattribute-method-string)

</td><td>

Completely remove an attribute

</td></tr>
<tr><td>

[DeleteAttribute(IIntelliPidElement, String)](#ibuilderintellipidattributeaccessor.deleteattribute-method-iintellipidelement-string)

</td><td>

Remove attribute from element

</td></tr>
<tr><td>

[FindElementsByAttributes](#ibuilderintellipidattributeaccessor.findelementsbyattributes-method) 

</td><td>

Find all elements by condition

</td></tr>
<tr><td>

[GetAttributes](#ibuilderintellipidattributeaccessor.getattributes-method)

</td><td>

Extract all attribute values of an element

</td></tr>
<tr><td>

[IterateAttributes](#ibuilderintellipidattributeaccessor.iterateattributes-method)

</td><td>

Iterate over all attribute keys

</td></tr>
<tr><td>

[RenameAttribute(String, String)](#ibuilderintellipidattributeaccessor.renameattribute-method-string-string)

</td><td>

Change an attribute key for all elements

</td></tr>
<tr><td>

[RenameAttribute(IIntelliPidElement, String, String)](#ibuilderintellipidattributeaccessor.renameattribute-method-iintellipidelement-string-string)

</td><td>

Rename existing attribute in element

</td></tr>
<tr><td>

[UpdateAttribute](#ibuilderintellipidattributeaccessor.updateattribute-method)

</td><td>

Update existing attribute in element

</td></tr>
</table>

### See Also

[IBuilderIntelliPidAttributeAccessor
Interface](#ibuilderintellipidattributeaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderIntelliPidAttributeAccessor.AddAttribute Method 

Add a new attribute to an element

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  --------------------------------
  C\#
  --------------------------------
  void AddAttribute(\
  IIntelliPidElement *element*,\
  string *name*,\
  string *value*\
  )

  --------------------------------

#### Parameters

*element*

Type:
[UniversalPlantViewerBuilderPlugin.Version1.IIntelliPidElement](#iintellipidelement-interface)

The element

*name*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The name of the new attribute

*value*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The value of the new attribute

### See Also

[IBuilderIntelliPidAttributeAccessor
Interface](#ibuilderintellipidattributeaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderIntelliPidAttributeAccessor.DeleteAttribute Method 

### Overload List

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[DeleteAttribute(String)](#ibuilderintellipidattributeaccessor.deleteattribute-method-string)

</td><td>

Completely remove an attribute

</td></tr>
<tr><td>

[DeleteAttribute(IIntelliPidElement, String)](#ibuilderintellipidattributeaccessor.deleteattribute-method-iintellipidelement-string)

</td><td>

Remove attribute from element

</td></tr>
</table>

## See Also

[IBuilderIntelliPidAttributeAccessor
Interface](#ibuilderintellipidattributeaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderIntelliPidAttributeAccessor.DeleteAttribute Method (String)

Completely remove an attribute

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  ------------------------
  C\#
  ------------------------
  void DeleteAttribute(\
  string *name*\
  )

  ------------------------

#### Parameters

*name*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The name of the attribute to remove

### See Also

[IBuilderIntelliPidAttributeAccessor
Interface](#ibuilderintellipidattributeaccessor-interface)

[DeleteAttribute
Overload](#ibuilderintellipidattributeaccessor.deleteattribute-method)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderIntelliPidAttributeAccessor.DeleteAttribute Method (IIntelliPidElement, String)

Remove attribute from element

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  --------------------------------
  C\#
  --------------------------------
  void DeleteAttribute(\
  IIntelliPidElement *element*,\
  string *name*\
  )

  --------------------------------

#### Parameters

*element*

Type:
[UniversalPlantViewerBuilderPlugin.Version1.IIntelliPidElement](#iintellipidelement-interface)

The element

*name*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The name of the attribute to be removed

### See Also

[IBuilderIntelliPidAttributeAccessor
Interface](#ibuilderintellipidattributeaccessor-interface)

[DeleteAttribute
Overload](#ibuilderintellipidattributeaccessor.deleteattribute-method)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderIntelliPidAttributeAccessor.FindElementsByAttributes Method 

Find all elements by condition

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  --------------------------------------------------------------
  C\#
  --------------------------------------------------------------
  IEnumerable\<IIntelliPidElement\> FindElementsByAttributes(\
  string *conditionQuery*\
  )

  --------------------------------------------------------------

#### Parameters

*conditionQuery*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The filter condition (e.g. \"Name=Value\")

#### Return Value

Type:
[IEnumerable](http://msdn2.microsoft.com/en-us/library/9eekhta0)([IIntelliPidElement](#iintellipidelement-interface))**\[Missing
\<returns\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderIntelliPidAttributeAccessor.FindElementsByAttributes(System.String)\"\]**

### See Also

[IBuilderIntelliPidAttributeAccessor
Interface](#ibuilderintellipidattributeaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderIntelliPidAttributeAccessor.GetAttributes Method 

Extract all attribute values of an element

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  -----------------------------------------------
  C\#
  -----------------------------------------------
  IDictionary\<string, string\> GetAttributes(\
  IIntelliPidElement *element*,\
  HashSet\<string\> *attributes* = null\
  )

  -----------------------------------------------

#### Parameters

*element*

Type:
[UniversalPlantViewerBuilderPlugin.Version1.IIntelliPidElement](#iintellipidelement-interface)

The element

*attributes* (Optional)

Type:
[System.Collections.Generic.HashSet](http://msdn2.microsoft.com/en-us/library/bb359438)([String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf))

The attributes to extract (can be null in order to extract all
attributes)

#### Return Value

Type:
[IDictionary](http://msdn2.microsoft.com/en-us/library/s4ys34ea)([String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf),
[String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf))**\[Missing
\<returns\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderIntelliPidAttributeAccessor.GetAttributes(UniversalPlantViewerBuilderPlugin.Version1.IIntelliPidElement,System.Collections.Generic.HashSet{System.String})\"\]**

### See Also

[IBuilderIntelliPidAttributeAccessor
Interface](#ibuilderintellipidattributeaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderIntelliPidAttributeAccessor.IterateAttributes Method 

Iterate over all attribute keys

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

## Syntax

  C\#
  -------------------------------------------
  IEnumerable\<string\> IterateAttributes()

#### Return Value

Type:
[IEnumerable](http://msdn2.microsoft.com/en-us/library/9eekhta0)([String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf))**\[Missing
\<returns\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderIntelliPidAttributeAccessor.IterateAttributes\"\]**

### See Also

[IBuilderIntelliPidAttributeAccessor
Interface](#ibuilderintellipidattributeaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderIntelliPidAttributeAccessor.RenameAttribute Method 

### Overload List

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[RenameAttribute(String, String)](#ibuilderintellipidattributeaccessor.renameattribute-method-string-string)

</td><td>

Change an attribute key for all elements

</td></tr>
<tr><td>

[RenameAttribute(IIntelliPidElement, String, String)](#ibuilderintellipidattributeaccessor.renameattribute-method-iintellipidelement-string-string)

</td><td>

Rename existing attribute in element

</td></tr>
</table>

### See Also

[IBuilderIntelliPidAttributeAccessor
Interface](#ibuilderintellipidattributeaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderIntelliPidAttributeAccessor.RenameAttribute Method (String, String)

Change an attribute key for all elements

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  -------------------------
  C\#
  -------------------------
  void RenameAttribute(\
  string *originalName*,\
  string *newName*\
  )

  -------------------------

#### Parameters

*originalName*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The old name of the attribute

*newName*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The new name of the attribute

### See Also

[IBuilderIntelliPidAttributeAccessor
Interface](#ibuilderintellipidattributeaccessor-interface)

[RenameAttribute
Overload](#ibuilderintellipidattributeaccessor.renameattribute-method)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderIntelliPidAttributeAccessor.RenameAttribute Method (IIntelliPidElement, String, String)

Rename existing attribute in element

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  --------------------------------
  C\#
  --------------------------------
  void RenameAttribute(\
  IIntelliPidElement *element*,\
  string *oldName*,\
  string *newName*\
  )

  --------------------------------

#### Parameters

*element*

Type:
[UniversalPlantViewerBuilderPlugin.Version1.IIntelliPidElement](#iintellipidelement-interface)

The element

*oldName*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The old name of the attribute to be renamed

*newName*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The new name of the attribute

### See Also

[IBuilderIntelliPidAttributeAccessor
Interface](#ibuilderintellipidattributeaccessor-interface)

[RenameAttribute
Overload](#ibuilderintellipidattributeaccessor.renameattribute-method)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderIntelliPidAttributeAccessor.UpdateAttribute Method 

Update existing attribute in element

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  --------------------------------
  C\#
  --------------------------------
  void UpdateAttribute(\
  IIntelliPidElement *element*,\
  string *name*,\
  string *value*\
  )

  --------------------------------

#### Parameters

*element*

Type:
[UniversalPlantViewerBuilderPlugin.Version1.IIntelliPidElement](#iintellipidelement-interface)

The element

*name*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The name of the existing attribute

*value*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The new value

### See Also

[IBuilderIntelliPidAttributeAccessor
Interface](#ibuilderintellipidattributeaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderIntelliPidElementAccessor Interface

**\[Missing \<summary\> documentation for
\"T:UniversalPlantViewerBuilderPlugin.Version1.IBuilderIntelliPidElementAccessor\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  ----------------------------------------------------
  public interface IBuilderIntelliPidElementAccessor

The **IBuilderIntelliPidElementAccessor** type exposes the following
members.

### Properties

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[Sessions](#ibuilderintellipidelementaccessor.sessions-property)

</td><td>

</td></tr>
</table>

### Methods

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[GetElement](#ibuilderintellipidelementaccessor.getelement-method)

</td><td>

Access a single element

</td></tr>
<tr><td>

[IterateElements](#ibuilderintellipidelementaccessor.iterateelements-method)

</td><td>

Iterate over all elements

</td></tr>
</table>

### See Also

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderIntelliPidElementAccessor.IBuilderIntelliPidElementAccessor Properties

The
[IBuilderIntelliPidElementAccessor](#ibuilderintellipidelementaccessor-interface)
type exposes the following members.

### Properties

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[Sessions](#ibuilderintellipidelementaccessor.sessions-property)

</td><td>

</td></tr>
</table>

### See Also

[IBuilderIntelliPidElementAccessor
Interface](#ibuilderintellipidelementaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderIntelliPidElementAccessor.Sessions Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.IBuilderIntelliPidElementAccessor.Sessions\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -------------------------------------------
  IEnumerable\<ISession\> Sessions { get; }

#### Property Value

Type:
[IEnumerable](http://msdn2.microsoft.com/en-us/library/9eekhta0)([ISession](#isession-interface))

### See Also

[IBuilderIntelliPidElementAccessor
Interface](#ibuilderintellipidelementaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderIntelliPidElementAccessor.IBuilderIntelliPidElementAccessor Methods

The
[IBuilderIntelliPidElementAccessor](#ibuilderintellipidelementaccessor-interface)
type exposes the following members.

### Methods

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[GetElement](#ibuilderintellipidelementaccessor.getelement-method)

</td><td>

Access a single element

</td></tr>
<tr><td>

[IterateElements](#ibuilderintellipidelementaccessor.iterateelements-method)

</td><td>

Iterate over all elements

</td></tr>
</table>

### See Also

[IBuilderIntelliPidElementAccessor
Interface](#ibuilderintellipidelementaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderIntelliPidElementAccessor.GetElement Method 

Access a single element

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  ---------------------------------
  C\#
  ---------------------------------
  IIntelliPidElement GetElement(\
  Guid *uid*\
  )

  ---------------------------------

#### Parameters

*uid*

Type: [System.Guid](http://msdn2.microsoft.com/en-us/library/cey1zx63)

The uid of the element

#### Return Value

Type: [IIntelliPidElement](#iintellipidelement-interface)**\[Missing
\<returns\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderIntelliPidElementAccessor.GetElement(System.Guid)\"\]**

### See Also

[IBuilderIntelliPidElementAccessor
Interface](#ibuilderintellipidelementaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderIntelliPidElementAccessor.IterateElements Method 

Iterate over all elements

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -----------------------------------------------------
  IEnumerable\<IIntelliPidElement\> IterateElements()

#### Return Value

Type:
[IEnumerable](http://msdn2.microsoft.com/en-us/library/9eekhta0)([IIntelliPidElement](#iintellipidelement-interface))**\[Missing
\<returns\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderIntelliPidElementAccessor.IterateElements\"\]**

### See Also

[IBuilderIntelliPidElementAccessor
Interface](#ibuilderintellipidelementaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderLinkAccessor Interface

**\[Missing \<summary\> documentation for
\"T:UniversalPlantViewerBuilderPlugin.Version1.IBuilderLinkAccessor\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  ---------------------------------------
  public interface IBuilderLinkAccessor

The **IBuilderLinkAccessor** type exposes the following members.

### Methods

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[AddRelation](#ibuilderlinkaccessor.addrelation-method)

</td><td>

</td></tr>
<tr><td>

[CreateLink](#ibuilderlinkaccessor.createlink-method)

</td><td>

Create a new link - Use the returned identifier to register elements to the link

</td></tr>
</table>         

### See Also

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderLinkAccessor.IBuilderLinkAccessor Methods

The [IBuilderLinkAccessor](#ibuilderlinkaccessor-interface) type exposes
the following members.

### Methods

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[AddRelation](#ibuilderlinkaccessor.addrelation-method)

</td><td>

</td></tr>
<tr><td>

[CreateLink](#ibuilderlinkaccessor.createlink-method)

</td><td>

Create a new link - Use the returned identifier to register elements to the link

</td></tr>
</table>

### See Also

[IBuilderLinkAccessor Interface](#ibuilderlinkaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderLinkAccessor.AddRelation Method 

**\[Missing \<summary\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderLinkAccessor.AddRelation(UniversalPlantViewerBuilderPlugin.Version1.ILink,UniversalPlantViewerBuilderPlugin.Version1.IElement)\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  ---------------------
  C\#
  ---------------------
  void AddRelation(\
  ILink *link*,\
  IElement *element*\
  )

  ---------------------

#### Parameters

*link*

Type:
[UniversalPlantViewerBuilderPlugin.Version1.ILink](#ilink-interface)

**\[Missing \<param name=\"link\"/\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderLinkAccessor.AddRelation(UniversalPlantViewerBuilderPlugin.Version1.ILink,UniversalPlantViewerBuilderPlugin.Version1.IElement)\"\]**

*element*

Type:
[UniversalPlantViewerBuilderPlugin.Version1.IElement](#ielement-interface)

**\[Missing \<param name=\"element\"/\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderLinkAccessor.AddRelation(UniversalPlantViewerBuilderPlugin.Version1.ILink,UniversalPlantViewerBuilderPlugin.Version1.IElement)\"\]**

### See Also

[IBuilderLinkAccessor Interface](#ibuilderlinkaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IBuilderLinkAccessor.CreateLink Method 

Create a new link - Use the returned identifier to register elements to
the link

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  -------------------------------------
  C\#
  -------------------------------------
  ILink CreateLink(\
  string *url*,\
  Color *color*,\
  string *name* = null,\
  LinkType *type* = LinkType.Default\
  )

  -------------------------------------

#### Parameters

*url*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The url of the new link

*color*

Type:
[UniversalPlantViewerBuilderPlugin.Version1.Color](#color-structure)

The color of the new link

*name* (Optional)

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The name of the new link - optional

*type* (Optional)

Type:
[UniversalPlantViewerBuilderPlugin.Version1.LinkType](#linktype-enumeration)

Special link treatment configuration

#### Return Value

Type: [ILink](#ilink-interface)**\[Missing \<returns\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IBuilderLinkAccessor.CreateLink(System.String,UniversalPlantViewerBuilderPlugin.Version1.Color,System.String,UniversalPlantViewerBuilderPlugin.Version1.LinkType)\"\]**

### See Also

[IBuilderLinkAccessor Interface](#ibuilderlinkaccessor-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IDocument Interface

**\[Missing \<summary\> documentation for
\"T:UniversalPlantViewerBuilderPlugin.Version1.IDocument\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  ----------------------------
  public interface IDocument

### See Also

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IElement Interface

**\[Missing \<summary\> documentation for
\"T:UniversalPlantViewerBuilderPlugin.Version1.IElement\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  ---------------------------
  public interface IElement

The **IElement** type exposes the following members.

### Properties

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[Session](#ielement.session-property)

</td><td>

</td></tr>
<tr><td>

[Uid](#ielement.uid-property)

</td><td>

</td></tr>
</table>

### Methods

<table>
  <thead>
    <tr>
      <th></th>
      <th><p>Name</p></th>
      <th><p>Description</p></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td></td>
      <td><p><a href="#ielement.addattribute-method">AddAttribute</a></p></td>
      <td><p><strong>Obsolete.</strong></p>
      <p>Add a new attribute to an
      element</p></td>
    </tr>
    <tr>
      <td></td>
      <td><p><a href="#ielement.deleteattribute-method">DeleteAttribute</a></p></td>
      <td><p><strong>Obsolete.</strong></p>
      <p>Remove attribute from element</p></td>
    </tr>
    <tr>
      <td></td>
      <td><p><a href="#ielement.getattributes-method">GetAttributes</a></p></td>
      <td><p><strong>Obsolete.</strong></p>
      <p>Extract all attribute values
      of an element</p></td>
    </tr>
    <tr>
      <td></td>
      <td><p><a href="#ielement.renameattribute-method">RenameAttribute</a></p></td>
      <td><p><strong>Obsolete.</strong></p>
      <p>Rename existing attribute in
      element</p></td>
    </tr>
    <tr>
      <td></td>
      <td><p><a href="#ielement.updateattribute-method">UpdateAttribute</a></p></td>
      <td><p><strong>Obsolete.</strong></p>
      <p>Update existing attribute in
      element</p></td>
    </tr>
  </tbody>
</table>

### See Also

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IElement.IElement Properties

The [IElement](#ielement-interface) type exposes the following members.

### Properties

     Name                                    Description
  -- --------------------------------------- -------------
     [Session](#ielement.session-property)   
     [Uid](#ielement.uid-property)           

### See Also

[IElement Interface](#ielement-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IElement.Session Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.IElement.Session\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -------------------------
  string Session { get; }

#### Property Value

Type: [String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

### See Also

[IElement Interface](#ielement-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IElement.Uid Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.IElement.Uid\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -------------------
  Guid Uid { get; }

#### Property Value

Type: [Guid](http://msdn2.microsoft.com/en-us/library/cey1zx63)

### See Also

[IElement Interface](#ielement-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IElement.IElement Methods

The [IElement](#ielement-interface) type exposes the following members.

### Methods

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[AddAttribute](#ielement.addattribute-method)

</td><td>

**Obsolete.** Add a new attribute to an element

</td></tr>
<tr><td>

[DeleteAttribute](#ielement.deleteattribute-method)

</td><td>

**Obsolete.** Remove attribute from element

</td></tr>
<tr><td>

[GetAttributes](#ielement.getattributes-method)

</td><td>

**Obsolete.** Extract all attribute values of an element

</td></tr>
<tr><td>

[RenameAttribute](#ielement.renameattribute-method)

</td><td>

**Obsolete.** Rename existing attribute in element

</td></tr>
<tr><td>

[UpdateAttribute](#ielement.updateattribute-method)

</td><td>

**Obsolete.** Update existing attribute in element

</td></tr>
</table>

### See Also

[IElement Interface](#ielement-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IElement.AddAttribute Method 

> This API is now obsolete.

Add a new attribute to an element

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  ------------------------
  C\#
  ------------------------
  \[ObsoleteAttribute\]\
  void AddAttribute(\
  string *name*,\
  string *value*\
  )

  ------------------------

#### Parameters

*name*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The new attribute key

*value*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The attributes value

### See Also

[IElement Interface](#ielement-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IElement.DeleteAttribute Method 

> This API is now obsolete.

Remove attribute from element

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  ------------------------
  C\#
  ------------------------
  \[ObsoleteAttribute\]\
  void DeleteAttribute(\
  string *name*\
  )

  ------------------------

#### Parameters

*name*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The name of the attribute to be removed

### See Also

[IElement Interface](#ielement-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IElement.GetAttributes Method 

> This API is now obsolete.

Extract all attribute values of an element

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  -----------------------------------------------
  C\#
  -----------------------------------------------
  \[ObsoleteAttribute\]\
  IDictionary\<string, string\> GetAttributes(\
  HashSet\<string\> *attributes* = null\
  )

  -----------------------------------------------

#### Parameters

*attributes* (Optional)

Type:
[System.Collections.Generic.HashSet](http://msdn2.microsoft.com/en-us/library/bb359438)([String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf))

The attributes to extract (can be null in order to extract all
attributes)

#### Return Value

Type:
[IDictionary](http://msdn2.microsoft.com/en-us/library/s4ys34ea)([String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf),
[String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf))**\[Missing
\<returns\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IElement.GetAttributes(System.Collections.Generic.HashSet{System.String})\"\]**

### See Also

[IElement Interface](#ielement-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IElement.RenameAttribute Method 

> This API is now obsolete.

Rename existing attribute in element

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  ------------------------
  C\#
  ------------------------
  \[ObsoleteAttribute\]\
  void RenameAttribute(\
  string *oldName*,\
  string *newName*\
  )

  ------------------------

#### Parameters

*oldName*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The old name of the attribute to be renamed

*newName*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The new name of the attribute

### See Also

[IElement Interface](#ielement-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IElement.UpdateAttribute Method 

> This API is now obsolete.

Update existing attribute in element

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  ------------------------
  C\#
  ------------------------
  \[ObsoleteAttribute\]\
  void UpdateAttribute(\
  string *name*,\
  string *value*\
  )

  ------------------------

#### Parameters

*name*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The name of the existing attribute

*value*

Type: [System.String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

The new value

### See Also

[IElement Interface](#ielement-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IGraphicElement Interface

**\[Missing \<summary\> documentation for
\"T:UniversalPlantViewerBuilderPlugin.Version1.IGraphicElement\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  ----------------------------------
  public interface IGraphicElement

The **IGraphicElement** type exposes the following members.

## Properties

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[AspectName](#igraphicelement.aspectname-property)

</td><td>

</td></tr>
<tr><td>

[ParentElement](#igraphicelement.parentelement-property)

</td><td>

</td></tr>
</table>

### See Also

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IGraphicElement.IGraphicElement Properties

The [IGraphicElement](#igraphicelement-interface) type exposes the
following members.

### Properties

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[AspectName](#igraphicelement.aspectname-property)

</td><td>

</td></tr>
<tr><td>

[ParentElement](#igraphicelement.parentelement-property)

</td><td>

</td></tr>
</table>

### See Also

[IGraphicElement Interface](#igraphicelement-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IGraphicElement.AspectName Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.IGraphicElement.AspectName\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  ----------------------------
  string AspectName { get; }

#### Property Value

Type: [String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

### See Also

[IGraphicElement Interface](#igraphicelement-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IGraphicElement.ParentElement Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.IGraphicElement.ParentElement\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  ---------------------------------
  IElement ParentElement { get; }

#### Property Value

Type: [IElement](#ielement-interface)

### See Also

[IGraphicElement Interface](#igraphicelement-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IIntelliPidElement Interface

**\[Missing \<summary\> documentation for
\"T:UniversalPlantViewerBuilderPlugin.Version1.IIntelliPidElement\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -------------------------------------
  public interface IIntelliPidElement

The **IIntelliPidElement** type exposes the following members.

### Properties

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[Session](#iintellipidelement.session-property)

</td><td>

</td></tr>
<tr><td>

[Uid](#iintellipidelement.uid-property)

</td><td>

</td></tr>
</table>

### See Also

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IIntelliPidElement.IIntelliPidElement Properties

The [IIntelliPidElement](#iintellipidelement-interface) type exposes the
following members.

### Properties

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[Session](#iintellipidelement.session-property)

</td><td>

</td></tr>
<tr><td>

[Uid](#iintellipidelement.uid-property)

</td><td>

</td></tr>
</table>             

### See Also

[IIntelliPidElement Interface](#iintellipidelement-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IIntelliPidElement.Session Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.IIntelliPidElement.Session\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -------------------------
  string Session { get; }

#### Property Value

Type: [String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

### See Also

[IIntelliPidElement Interface](#iintellipidelement-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IIntelliPidElement.Uid Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.IIntelliPidElement.Uid\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -------------------
  Guid Uid { get; }

#### Property Value

Type: [Guid](http://msdn2.microsoft.com/en-us/library/cey1zx63)

### See Also

[IIntelliPidElement Interface](#iintellipidelement-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## ILink Interface

**\[Missing \<summary\> documentation for
\"T:UniversalPlantViewerBuilderPlugin.Version1.ILink\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  ------------------------
  public interface ILink

### See Also

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IPluginVersion1 Interface

**\[Missing \<summary\> documentation for
\"T:UniversalPlantViewerBuilderPlugin.Version1.IPluginVersion1\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  ----------------------------------
  public interface IPluginVersion1

The **IPluginVersion1** type exposes the following members.

### Properties

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[Name](#ipluginversion1.name-property)

</td><td>

</td></tr>
<tr><td>

[ProcessOrder](#ipluginversion1.processorder-property)

</td><td>

</td></tr>
</table>

### Methods

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[BeforeExport](#ipluginversion1.beforeexport-method)

</td><td>

</td></tr>
</table>

### See Also

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IPluginVersion1.IPluginVersion1 Properties

The [IPluginVersion1](#ipluginversion1-interface) type exposes the
following members.

### Properties

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[Name](#ipluginversion1.name-property)

</td><td>

</td></tr>
<tr><td>

[ProcessOrder](#ipluginversion1.processorder-property)

</td><td>

</td></tr>
</table>

### See Also

[IPluginVersion1 Interface](#ipluginversion1-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IPluginVersion1.Name Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.IPluginVersion1.Name\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  ----------------------
  string Name { get; }

#### Property Value

Type: [String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

### See Also

[IPluginVersion1 Interface](#ipluginversion1-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IPluginVersion1.ProcessOrder Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.IPluginVersion1.ProcessOrder\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  ---------------------------
  int ProcessOrder { get; }

#### Property Value

Type: [Int32](http://msdn2.microsoft.com/en-us/library/td2s409d)

### See Also

[IPluginVersion1 Interface](#ipluginversion1-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IPluginVersion1.IPluginVersion1 Methods

The [IPluginVersion1](#ipluginversion1-interface) type exposes the
following members.

### Methods

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[BeforeExport](#ipluginversion1.beforeexport-method)

</td><td>

</td></tr>
</table>

### See Also

[IPluginVersion1 Interface](#ipluginversion1-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IPluginVersion1.BeforeExport Method 

**\[Missing \<summary\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IPluginVersion1.BeforeExport(UniversalPlantViewerBuilderPlugin.Version1.IPreExportHook)\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  ------------------------
  C\#
  ------------------------
  void BeforeExport(\
  IPreExportHook *hook*\
  )

  ------------------------

#### Parameters

*hook*

Type:
[UniversalPlantViewerBuilderPlugin.Version1.IPreExportHook](#ipreexporthook-interface)

**\[Missing \<param name=\"hook\"/\> documentation for
\"M:UniversalPlantViewerBuilderPlugin.Version1.IPluginVersion1.BeforeExport(UniversalPlantViewerBuilderPlugin.Version1.IPreExportHook)\"\]**

### See Also

[IPluginVersion1 Interface](#ipluginversion1-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IPreExportHook Interface

**\[Missing \<summary\> documentation for
\"T:UniversalPlantViewerBuilderPlugin.Version1.IPreExportHook\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  ---------------------------------
  public interface IPreExportHook

The **IPreExportHook** type exposes the following members.

### Properties

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[BuilderAttributeAccessor](#ipreexporthook.builderattributeaccessor-property)

</td><td>

</td></tr>
<tr><td>

[BuilderContext](#ipreexporthook.buildercontext-property)

</td><td>

</td></tr>
<tr><td>

[BuilderDocumentAccessor](#ipreexporthook.builderdocumentaccessor-property)

</td><td>

</td></tr>
<tr><td>

[BuilderElementAccessor](#ipreexporthook.builderelementaccessor-property)

</td><td>

</td></tr>
<tr><td>

[BuilderGraphicAccessor](#ipreexporthook.buildergraphicaccessor-property)

</td><td>

</td></tr>
<tr><td>

[BuilderIntelliPidAttributeAccessor](#ipreexporthook.builderintellipidattributeaccessor-property)

</td><td>

</td></tr>
<tr><td>

[BuilderIntelliPidElementAccessor](#ipreexporthook.builderintellipidelementaccessor-property)

</td><td>

</td></tr>
<tr><td>

[BuilderLinkAccessor](#ipreexporthook.builderlinkaccessor-property)

</td><td>

</td></tr>
</table>    

### See Also

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IPreExportHook.IPreExportHook Properties

The [IPreExportHook](#ipreexporthook-interface) type exposes the
following members.

### Properties

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[BuilderAttributeAccessor](#ipreexporthook.builderattributeaccessor-property)

</td><td>

</td></tr>
<tr><td>

[BuilderContext](#ipreexporthook.buildercontext-property)

</td><td>

</td></tr>
<tr><td>

[BuilderDocumentAccessor](#ipreexporthook.builderdocumentaccessor-property)

</td><td>

</td></tr>
<tr><td>

[BuilderElementAccessor](#ipreexporthook.builderelementaccessor-property)

</td><td>

</td></tr>
<tr><td>

[BuilderGraphicAccessor](#ipreexporthook.buildergraphicaccessor-property)

</td><td>

</td></tr>
<tr><td>

[BuilderIntelliPidAttributeAccessor](#ipreexporthook.builderintellipidattributeaccessor-property)

</td><td>

</td></tr>
<tr><td>

[BuilderIntelliPidElementAccessor](#ipreexporthook.builderintellipidelementaccessor-property)

</td><td>

</td></tr>
<tr><td>

[BuilderLinkAccessor](#ipreexporthook.builderlinkaccessor-property)

</td><td>

</td></tr>
</table>

### See Also

[IPreExportHook Interface](#ipreexporthook-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IPreExportHook.BuilderAttributeAccessor Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.IPreExportHook.BuilderAttributeAccessor\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -------------------------------------------------------------
  IBuilderAttributeAccessor BuilderAttributeAccessor { get; }

#### Property Value

Type: [IBuilderAttributeAccessor](#ibuilderattributeaccessor-interface)

### See Also

[IPreExportHook Interface](#ipreexporthook-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IPreExportHook.BuilderContext Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.IPreExportHook.BuilderContext\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -----------------------------------------
  IBuilderContext BuilderContext { get; }

#### Property Value

Type: [IBuilderContext](#ibuildercontext-interface)

### See Also

[IPreExportHook Interface](#ipreexporthook-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IPreExportHook.BuilderDocumentAccessor Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.IPreExportHook.BuilderDocumentAccessor\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -----------------------------------------------------------
  IBuilderDocumentAccessor BuilderDocumentAccessor { get; }

#### Property Value

Type: [IBuilderDocumentAccessor](#ibuilderdocumentaccessor-interface)

### See Also

[IPreExportHook Interface](#ipreexporthook-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IPreExportHook.BuilderElementAccessor Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.IPreExportHook.BuilderElementAccessor\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  ---------------------------------------------------------
  IBuilderElementAccessor BuilderElementAccessor { get; }

#### Property Value

Type: [IBuilderElementAccessor](#ibuilderelementaccessor-interface)

### See Also

[IPreExportHook Interface](#ipreexporthook-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IPreExportHook.BuilderGraphicAccessor Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.IPreExportHook.BuilderGraphicAccessor\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  ---------------------------------------------------------
  IBuilderGraphicAccessor BuilderGraphicAccessor { get; }

#### Property Value

Type: [IBuilderGraphicAccessor](#ibuildergraphicaccessor-interface)

### See Also

[IPreExportHook Interface](#ipreexporthook-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IPreExportHook.BuilderIntelliPidAttributeAccessor Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.IPreExportHook.BuilderIntelliPidAttributeAccessor\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  ---------------------------------------------------------------------------------
  IBuilderIntelliPidAttributeAccessor BuilderIntelliPidAttributeAccessor { get; }

#### Property Value

Type:
[IBuilderIntelliPidAttributeAccessor](#ibuilderintellipidattributeaccessor-interface)

### See Also

[IPreExportHook Interface](#ipreexporthook-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

# IPreExportHook.BuilderIntelliPidElementAccessor Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.IPreExportHook.BuilderIntelliPidElementAccessor\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -----------------------------------------------------------------------------
  IBuilderIntelliPidElementAccessor BuilderIntelliPidElementAccessor { get; }

#### Property Value

Type:
[IBuilderIntelliPidElementAccessor](#ibuilderintellipidelementaccessor-interface)

### See Also

[IPreExportHook Interface](#ipreexporthook-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## IPreExportHook.BuilderLinkAccessor Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.IPreExportHook.BuilderLinkAccessor\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  ---------------------------------------------------
  IBuilderLinkAccessor BuilderLinkAccessor { get; }

#### Property Value

Type: [IBuilderLinkAccessor](#ibuilderlinkaccessor-interface)

### See Also

[IPreExportHook Interface](#ipreexporthook-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## ISession Interface

**\[Missing \<summary\> documentation for
\"T:UniversalPlantViewerBuilderPlugin.Version1.ISession\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  ---------------------------
  public interface ISession

The **ISession** type exposes the following members.

### Properties

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[SessionFile](#isession.sessionfile-property)

</td><td>

</td></tr>
</table>

### See Also

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## ISession.ISession Properties

The [ISession](#isession-interface) type exposes the following members.

### Properties

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[SessionFile](#isession.sessionfile-property)

</td><td>

</td></tr>
</table>

### See Also

[ISession Interface](#isession-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## ISession.SessionFile Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.ISession.SessionFile\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -----------------------------
  string SessionFile { get; }

#### Property Value

Type: [String](http://msdn2.microsoft.com/en-us/library/s1wwdcbf)

### See Also

[ISession Interface](#isession-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## ISessionTransformation Interface

**\[Missing \<summary\> documentation for
\"T:UniversalPlantViewerBuilderPlugin.Version1.ISessionTransformation\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -----------------------------------------
  public interface ISessionTransformation

The **ISessionTransformation** type exposes the following members.

### Properties

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[Rotation](#isessiontransformation.rotation-property)

</td><td>

</td></tr>
<tr><td>

[Scale](#isessiontransformation.scale-property)

</td><td>

</td></tr>
<tr><td>

[Source](#isessiontransformation.source-property)

</td><td>

</td></tr>
<tr><td>

[Translation](#isessiontransformation.translation-property)

</td><td>

</td></tr>
</table>

### See Also

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## ISessionTransformation.ISessionTransformation Properties

The [ISessionTransformation](#isessiontransformation-interface) type
exposes the following members.

### Properties

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[Rotation](#isessiontransformation.rotation-property)

</td><td>

</td></tr>
<tr><td>

[Scale](#isessiontransformation.scale-property)

</td><td>

</td></tr>
<tr><td>

[Source](#isessiontransformation.source-property)

</td><td>

</td></tr>
<tr><td>

[Translation](#isessiontransformation.translation-property)

</td><td>

</td></tr>
</table>

### See Also

[ISessionTransformation Interface](#isessiontransformation-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

# ISessionTransformation.Rotation Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.ISessionTransformation.Rotation\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  --------------------------
  double Rotation { get; }

#### Property Value

Type: [Double](http://msdn2.microsoft.com/en-us/library/643eft0t)

### See Also

[ISessionTransformation Interface](#isessiontransformation-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## ISessionTransformation.Scale Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.ISessionTransformation.Scale\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -----------------------
  double Scale { get; }

#### Property Value

Type: [Double](http://msdn2.microsoft.com/en-us/library/643eft0t)

### See Also

[ISessionTransformation Interface](#isessiontransformation-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## ISessionTransformation.Source Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.ISessionTransformation.Source\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  --------------------------
  Vector3D Source { get; }

#### Property Value

Type: [Vector3D](#vector3d-structure)

### See Also

[ISessionTransformation Interface](#isessiontransformation-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## ISessionTransformation.Translation Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.ISessionTransformation.Translation\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -------------------------------
  Vector3D Translation { get; }

#### Property Value

Type: [Vector3D](#vector3d-structure)

### See Also

[ISessionTransformation Interface](#isessiontransformation-interface)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## LinkType Enumeration

**\[Missing \<summary\> documentation for
\"T:UniversalPlantViewerBuilderPlugin.Version1.LinkType\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  ----------------------
  public enum LinkType

### Members

<table>
<tr><th>Member name</th><th>Value</th><th>Description</th></tr>
<tr><td>

**Default**

</td><td>

0

</td><td>

No special treatment

</td></tr>
<tr><td>

**Pdf**

</td><td>

1

</td><td>

Opens the embedded pdf viewer with the link content

</td></tr>
<tr><td>

**Event**

</td><td>

2

</td><td>

Only triggers the api event. Will not open the link

</td></tr>
</table>

### See Also

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## Vector3D Structure

**\[Missing \<summary\> documentation for
\"T:UniversalPlantViewerBuilderPlugin.Version1.Vector3D\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

## Syntax

  C\#
  ------------------------
  public struct Vector3D

The **Vector3D** type exposes the following members.

### Properties

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[X](#vector3d.x-property)

</td><td>

</td></tr>
<tr><td>

[Y](#vector3d.y-property)

</td><td>

</td></tr>
<tr><td>

[Z](#vector3d.z-property)

</td><td>

</td></tr>
</table>

### See Also

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

# Vector3D.Vector3D Properties

The [Vector3D](#vector3d-structure) type exposes the following members.

### Properties

<table>
<tr><th>Name</th><th>Description</th></tr>
<tr><td>

[X](#vector3d.x-property)

</td><td>

</td></tr>
<tr><td>

[Y](#vector3d.y-property)

</td><td>

</td></tr>
<tr><td>

[Z](#vector3d.z-property)

</td><td>

</td></tr>
</table>

### See Also

[Vector3D Structure](#vector3d-structure)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## Vector3D.X Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.Vector3D.X\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -------------------------------
  public double X { get; set; }

#### Property Value

Type: [Double](http://msdn2.microsoft.com/en-us/library/643eft0t)

### See Also

[Vector3D Structure](#vector3d-structure)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## Vector3D.Y Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.Vector3D.Y\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -------------------------------
  public double Y { get; set; }

#### Property Value

Type: [Double](http://msdn2.microsoft.com/en-us/library/643eft0t)

### See Also

[Vector3D Structure](#vector3d-structure)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

## Vector3D.Z Property 

**\[Missing \<summary\> documentation for
\"P:UniversalPlantViewerBuilderPlugin.Version1.Vector3D.Z\"\]**

**Namespace:** [UniversalPlantViewerBuilderPlugin.Version1](#universalplantviewerbuilderplugin.version1-namespace)**Assembly:** UniversalPlantViewerBuilderPlugin
(in UniversalPlantViewerBuilderPlugin.dll) Version: 5.0.1811.501

### Syntax

  C\#
  -------------------------------
  public double Z { get; set; }

#### Property Value

Type: [Double](http://msdn2.microsoft.com/en-us/library/643eft0t)

## See Also

[Vector3D Structure](#vector3d-structure)

[UniversalPlantViewerBuilderPlugin.Version1
Namespace](#universalplantviewerbuilderplugin.version1-namespace)

---

# UDiTH Capture Station — LaserscanClippingPlugin

# LaserscanClippingPlugin

**LaserscanClippingPlugin** is a plugin used in UniversalPlantViewer Builder to manipulate laser scan data for UniversalPlantViewer.
It uses UPV session files (`.upvc`) exported from UniversalPlantViewer and merges clipping information with the original session file.

## Setup procedure

LaserscanClippingPlugin is not an executable plugin and does not include a setup routine. After downloading the file, extract the contents of the ZIP archive to the following directory:

`%AppData%\CAXperts\Universal Plant Viewer Builder\Plugins\`

If the `Plugins` folder does not exist, create it manually.

## Clipping Definitions

## Create Clipping Objects

Before clipping, the clipping areas must be defined in UniversalPlantViewer by using sketching objects. To create a new sketch, select the Sketching function from the main menu.

<img src="./media/LaserScanClippingPlugin_image6.png" width="32" height="187">

Select a sketching object from the right-hand menu and place it within the laser scan object. Most commonly, a box or a horizontal or vertical vessel is used to define a clipping box or clipping volume.

<img src="./media/LaserScanClippingPlugin_image7.png" width="248" height="185"> 

After the sketch has been placed, the sketching object can be moved, resized, or rotated about the Z-axis to match the required clipping result. The sketching objects can also be colourised, and their transparency can be adjusted to make identification and orientation easier.

<img src="./media/LaserScanClippingPlugin_image8.png" width="36" height="168">

<img src="./media/LaserScanClippingPlugin_image9.png" width="740" height="420">

## Clipping Attributes

To define a sketching object as a clipping object, select the sketch object and use the drop-down list for the **LaserScanClippingMode** attribute. The default value, **Ignore**, means that the object is not used for clipping.

<img src="./media/LaserScanClippingPlugin_ClippingModeDrowpdown.png">

## LaserScanClippingMode -- ClipOutside

With the **OuterBound** value set, everything **outside** this boundary item is cut away from the laser scan. Any other 3D data or sketches are not affected. This is useful, for example, when there are superficial parts outside the main target area of the laser scan that are considered irrelevant and should be removed.

<img src="./media/LaserScanClippingPlugin_image15.png" width="728" height="643">

Place a sketch item of type `box`, then move and scale it to cover the area you want to keep. In this example, the bounding box is coloured white and set to semi-transparent so that it remains visible while still allowing you to see through it.

<img src="./media/LaserScanClippingPlugin_image16.png" width="668" height="587">

After saving the sketch as a `.upvc` file and building it with UniversalPlantViewer Builder, only the laser scan elements within the clipping box remain.

<img src="./media/LaserScanClippingPlugin_image17.png" width="772" height="653">

## LaserScanClippingMode -- Isolate

With the **Isolate** value set, you can use different items to define a shape that is converted into an independent object as accurately as possible. This is useful, for example, if you want to isolate a pump from the laser scan and make it into a separate object.

<img src="./media/LaserScanClippingPlugin_image20.png" width="728" height="643">

Place the sketch items, then move and scale them so that they cover the volume belonging to the required item. The colour used for the sketching items becomes the colour assigned to the object after clipping.

<img src="./media/LaserScanClippingPlugin_image21.png" width="727" height="546">

When the sketch is saved as a `.upvc` file, the laser scan data located inside the shapes is cut from the laser scan and replaced by a separate object when the model is rebuilt with UniversalPlantViewer Builder. These objects can then be recoloured, hidden, or found individually through the search function by using their attributes.

<img src="./media/LaserScanClippingPlugin_image22.png" width="757" height="606">

The isolated objects can be given additional custom attributes. These attributes remain part of the object even after clipping, making it possible to attach data, links, and similar information to the resulting object. See the UniversalPlantViewer documentation for more information about custom attributes.

<img src="./media/LaserScanClippingPlugin_image10.png" width="300" height="240">

## LaserScanClippingMode -- Delete

With the **Delete** value set, everything inside this boundary item is cut out of the laser scan. Any other 3D data or sketches are not affected. This is useful, for example, when people moved through the laser scan and left artefacts that need to be removed.

<img src="./media/LaserScanClippingPlugin_image18.png" width="673" height="434">

Place a sketch item, then move and scale it to cover the volume you want to remove. In this example, the bounding box is coloured red and set to semi-transparent so that it can still be seen through.

<img src="./media/LaserScanClippingPlugin_image9.png" width="739" height="629">
 
When the sketch is saved as a `.upvc` file, the laser scan data located inside the shapes is cut from the laser scan when the model is rebuilt with UniversalPlantViewer Builder.

<img src="./media/LaserScanClippingPlugin_image19.png" width="746" height="639">

## LaserScanClippingMode -- Update

By using the value **Update**, specific parts of a laser scan can be committed to an existing laser scan, while the rest of the older scan remains unchanged.

To do this, the `UPVBPluginLaserscanClipping.xml` file must be edited.
The file can be found at:

`AppData\Roaming\CAXperts\Universal Plant Viewer Builder\Plugins\UPVBPluginLaserscanClipping.xml`

<img src="./media/LaserScanClippingPlugin_Update.png" width="599" height="193">

The tag `UpdateOldUPVC` must contain the name of the `.upvc` file for the model to be updated. `UpdateNewUPVC` must contain the name of the newer `.upvc` file containing the update-marked clipped objects. When these two files are built in UniversalPlantViewer Builder, the older model contains the clipped parts as they appear in the newer version, while the rest of the model remains unchanged.

## Save Sketch Definitions

It is strongly recommended to save the sketch definitions as UPV Sketch files (`.upvf`). This is not required for clipping, but it allows you to recall the sketch definitions, load them into later models, or edit them afterwards.

## Export Clipping Definitions

To use the clipping sketches you have created for the actual clipping process, the files must be saved as UPV core files (`.upvc`) for use with UniversalPlantViewer Builder. Make sure that `.upvc` is selected as the save format and that the files are saved in the same folder as the original source files used to build the model.

<img src="./media/LaserScanClippingPlugin_image25.png" width="730" height="171">

## UniversalPlantViewer Builder settings

To create the final clipped laser scan model, you must run UniversalPlantViewer Builder so that it can execute the clipping tasks defined by the clipping sketch items. To do this, start UPV Builder and click the **[Browse...]** button.

<img src="./media/LaserScanClippingPlugin_image26.png" width="736" height="358">

## Selecting Sessions

In the following file-open dialogue, select the original laser scan session and all clipping sessions that you want to integrate into the new model (`.upvc` files) while holding down the **Ctrl** key, then confirm by clicking **[Open]**.

<img src="./media/LaserScanClippingPlugin_image27.png" width="728" height="491">

A new dialogue appears, asking for a file name for saving the collection of multiple session files (`.upvm`). Enter a file name that you will recognise later.

<img src="./media/LaserScanClippingPlugin_image28.png" width="727" height="464">

## Activating the LaserscanClippingPlugin

For clipping to work, LaserscanClippingPlugin must first be activated. Go to **Tools → Settings** in the menu bar and select the **Plugins** tab.
Select the `UPVPluginLaserscanClipping` plugin from the available plugins, move it to the used plugins by clicking **[>]**, and confirm the selection with **[OK]**.

<img src="./media/LaserScanClippingPlugin_image29.png" width="739" height="352">

## Selecting additional Attributes

To integrate additional attributes, they must first be activated. Otherwise, they are ignored when the final clipped model is created. Go to **Tools → Settings** in the menu bar and select the **Attributes** tab. Select the attributes you want to use from the available attributes, move them to the used attributes by clicking **[>]**, and confirm the selection with **[OK]**.

<img src="./media/LaserScanClippingPlugin_image30.png" width="739" height="352">

## Output

Run UniversalPlantViewer Builder as usual. The resulting UPV contains the final clipped model.

---

# UDiTH Capture Station — ExcelImportPlugin

# ExcelImportPlugin

The **ExcelImportPlugin** is used to sideload additional attribute data not included in the initial source files. This feature helps to implement data from sources other than the original authoring systems and can contain any information, e.g. from third-party-sources or databases.

## Setup Procedure

The **ExcelImportPlugin** is not an executable plugin and comes without a setup routine. After the file download, unpack the content of the container (.zip) to **one** of the directories listed.

For current user use only:

%AppData%\\CAXperts\\UDiTH Builder\\Plugins\\

For all users (administrator rights might be required):

%programfiles%\\CAXperts\\UDiTH Builder\\Plugins\\

## Usage

## Attribute and Value Definition

The Attributes are defined in an Excel file following a fixed syntax:

![alt text](media/ExcelImportPlugin_image6.png)

**Source Attribute (UPVC)** -- The Source Attribute to look for when adding a new attribute.

**Source Value (UPVC)** -- Specifies a condition for the Source Attribute value.

**New Attribute** -- The new attribute you wish to define.

**New Value** -- The value to be stored under the new attribute.

By choosing a Source Attribute that applies to multiple objects, the new attribute is added to all objects containing that attribute with the defined Source Value.

Further **New Attribute** and **New Value** can be added to import multiple attributes

For importing attributes to IntelliPID the sheet in the Excel file needs to be named "IntelliPID_Import", for 3D the sheet should be called "Import".

## File Name and Placement

Multiple Excel files are supported. The ExcelImportPlugin will consume all Excel files within the "ExcelImport" folder next to the selected UPVC/UPVM file.

> This folder must be placed in the directory containing your .upvc files.

If this is the case, the files in the folder will automatically be considered once the plugin is activated in the UDiTH Builder

If the folder is not present, the file defined in the configuration file will be used. If that file is not found, either, the plugin will be ignored in the building process.

## Activating the ExcelImportPlugin

To import the additional attributes and values, the ExcelImport Plugin must be activated first. Go to **Tools → Settings** in the menu bar and select the **Plugins** tab. Select the "UPVPluginExcelImport" plugin from available plugins (fig. 2-1), transfer it to the used plugins by clicking on the **\[\>\]** button (fig. 2-2) and confirm your selection with **\[OK\]** (fig. 2-3).

<img src=".\media/ExcelImportPlugin_image7.png" width="778" height="370">

## Configuration File

The plugin is configurable through a configuration file named UPVPluginExcelImport.xml. This file is located alongside the .dll of the plugin (see chapter "Setup procedure"). It contains three values that can be set for configuration (fig. 3).

```xml
<?xml version="1.0" encoding="utf-8" ?>
<Config>
  <Folder>C:\ExcelImport</Folder>
  <SheetName>Import</SheetName>
  <SheetNameIntelliPID>IntelliPID_Import</SheetNameIntelliPID>
  <Debug>1</Debug>
  <ProcessOrder>100</ProcessOrder>
  <Threads>4</Threads>
</Config>

```

**Connection String** -- Will be used if no If no ExcelImport folder exists. If  ExcelImport folder and this file does not exist, the plugin is ignored.

**SheetName** -- Specifies the sheet to be used for import

**SheetNameIntelliPID** -- Specifies the sheet to be used for import for IntelliPid

**Debug** -- Activated the debugging mode with detailed information on the building process **0** for deactivated, **1** for activated.

**ProcessOrder** -- Process order of the plugin lowest first.

**Threads** -- Threads to use for processing.

---

# UDiTH SDK — UniversalPlantViewer SDK

# UDiTH SDK — user guide

This solution groups plugins that enrich or transform a plant model during a Builder export. End users work with the exported model in UDiTH; model preparers install and configure the plugins in a compatible CAXperts Builder. The solution itself is not a standalone application.

## Getting started

1. Obtain a plugin package compatible with your Builder from your administrator or supplier. Source projects alone are not an installer.
2. Have the required plugin DLLs, dependencies and configuration files installed in the Builder's plugin directory. This repository uses `%AppData%\CAXperts\<Builder>\Plugins` for its development deployment; the actual Builder folder depends on your installation.
3. Prepare the inputs described in the relevant guide. Replace sample database and workbook paths with your own accessible files.
4. Run the normal Builder export with the plugin installed. These plugins generally run before export; they do not provide their own desktop interface.
5. Check the Builder log, then open the exported model and inspect the resulting attributes, graphics or links. A plugin's “finished” message does not necessarily mean every item succeeded: several plugins log errors and continue.

Use a separate output when first trying plugins that delete, merge or rename model data. Removing a plugin affects future exports; it does not undo changes in an already exported model.

## Choose a tool

| User goal | Guide |
| --- | --- |
| Merge related objects | [Combine Objects](UPVBPluginCombineObjects.md) |
| Add or update attributes from workbooks | [Excel Import](UPVBPluginExcelImport.md) |
| Add visible text labels | [Graphics](UPVBPluginGraphics.md) |
| Clip or isolate laser-scan geometry | [Laserscan Clipping](UPVBPluginLaserscanClipping.md) |
| Transform attributes using rules | [Mapping](UPVBPluginMapping.md) |
| Standardize attribute names | [Rename Attributes](UPVBPluginRenameAttributes.md) |

## Configuration and support

For plugins using the shared configuration loader, the first input session's directory is searched before the installed plugin/helper directory; XML is preferred over JSON within each directory. Other plugins have different lookup rules, documented individually. `ProcessOrder`, where supported, controls ordering relative to other plugins; `Debug` enables additional logging.

These guides describe the checked-in implementation, not a verified compatibility matrix. If a plugin does not load, provide your administrator with the Builder version, plugin version and export log.

---

# UDiTH SDK — UPVBPluginCombineObjects

# Combine Objects — merge related model objects

Combines objects with shared attribute values into larger selectable objects, for example a pipe run or structural group. The export replaces the selected source objects with combined objects, changing selection granularity and object identity.

## Use

1. Install the plugin and its dependencies in your compatible Builder.
2. Copy and adapt [UPVBPluginCombineObjects.xml](UPVBPluginCombineObjects/UPVBPluginCombineObjects/UPVBPluginCombineObjects.xml). Use that filename beside the first input session or in the installed plugin/helper directory.
3. Add a `CombineConfig` for each grouping rule, using the settings below.
4. Export to a separate output and inspect a few groups. Check the log's identified and combined counts.

| Setting | Purpose |
| --- | --- |
| `Name` | Identifies the rule in logs and output attributes. |
| `Condition` | Selects objects, for example `Task=Piping`. |
| `AttributesToGroupBy` | Lists the attributes whose values define each group. |
| `AspectName` | Names the output graphic aspect. |
| `ExecutionOrder` | Orders the grouping rules. |
| `ExcludeConditions` | Leaves matching objects out, such as valves or instruments. |
| `HierachyLevel` | Limits path-based grouping depth; preserve this exact spelling. |

For a pipe-run workflow, group `Task=Piping` by `PipeRun` and `NPD`, and use exclusions for components that must remain individually selectable. The repository includes project-specific examples; their attribute names may not exist in your model.

## Results and checks

Combined objects carry `CombineMappingKey`, `CombineConfig` and `CombineCount`. Do not assume conflicting source attributes or source object IDs will remain available individually. If nothing combines, check selection conditions, group attributes and the configuration path reported in the log. The supplied default contains active area-specific rules; replace them before use on another plant.

[All solutions](UniversalPlantViewer.SDK.md)

---

# UDiTH SDK — UPVBPluginExcelImport

# Excel Import — add model attributes from spreadsheets

Adds or updates attributes on 3D and IntelliPID objects by matching workbook rows to existing attributes.

## Prepare a workbook

Use `.xlsx` files. In the `Import` sheet, row 1 supplies headers; columns A and B identify the attribute and value to match. From column C onward, headers are destination attribute names and cells are the values to write.

| Match attribute | Match value | Maintenance Zone | Owner |
| --- | --- | --- | --- |
| Name | P-101 | Zone A | Operations |
| Name | V-* | Zone B | Maintenance |

The first two column positions are significant; their header wording is not used for matching. `*` enables wildcard matching. Empty destination cells are skipped, so they do not clear existing values. Matching values can overwrite existing attributes.

For IntelliPID, use the same layout in `IntelliPID_Import`. Include an `Import` sheet even for an IntelliPID-only workbook: the current reader skips the workbook if the 3D sheet is absent.

## Run the import

1. Install the plugin and adapt [UPVBPluginExcelImport.xml](UPVBPluginExcelImport/UPVBPluginExcelImport/UPVBPluginExcelImport.xml).
2. Put workbooks in an `ExcelImport` folder beside the first input session. When that folder exists, it overrides the configured folder and uses the sheet names above.
3. Alternatively, set `Folder`, `SheetName` and `SheetNameIntelliPID` in the configuration. Place the configuration beside the first session or in the installed plugin/helper directory.
4. Close the workbooks, export, and inspect a known matched object's attributes.

Files starting with `~` are skipped. Avoid competing rows or files that assign different values to the same object and attribute. Check the logged folder, sheet names and matching attributes if data is missing. `Threads` controls concurrency; `Debug` enables more logging.

[All solutions](UniversalPlantViewer.SDK.md)

---

# UDiTH SDK — UPVBPluginGraphics

# Graphics — visible labels from model attributes

Creates text graphics from existing attribute values, organized into named aspects such as equipment or pipeline labels.

## Use

1. Install the plugin in a compatible Builder with [UPVBPluginGraphics.xml](UPVBPluginGraphics/UPVBPluginGraphics/UPVBPluginGraphics.xml) beside it. This initial configuration is needed when the plugin loads.
2. Edit its `Definitions`, or place a project-specific file with the same name beside the first input session to override it during export.
3. Export and open the output model. Show the configured label aspects and inspect representative objects.

| Definition field | What to enter |
| --- | --- |
| `Condition` | Object selection, for example `Task=Equipment`. |
| `LabelAttributeKey` | Attribute whose value becomes the label, for example `Name`. |
| `AspectName` | Label graphic aspect to create. |
| `Color` | `R`, `G`, `B`, `A` byte values from 0 to 255. |
| `TextHeightFactor` | Relative text sizing. |
| `MinimumSize` | Size cutoff; the general labeling path checks bounding-box volume. |
| `isRoutingItem` | Routing-specific handling, using the supplied example's `0` or `1`. |

The sample covers cableways, pipelines, equipment and valves. Replace conditions and attribute names to match your model. Missing label attributes are skipped. Pipeline conditions containing `Name=Pipe` use special geometry-based placement, so label behavior can differ from other definitions.

If labels are missing, check the configuration path in the log, the object's attributes, the size cutoff and aspect visibility. Reduce `TextHeightFactor` if labels obscure the model.

[All solutions](UniversalPlantViewer.SDK.md)

---

# UDiTH SDK — UPVBPluginLaserscanClipping

# Laserscan Clipping — trim and isolate scan geometry

Processes laser-scan geometry using clipping or spraying objects already present in the Builder inputs. Use it to remove unwanted scan regions or isolate regions as separate objects.

## Prepare and export

1. Install a compatible plugin package; this tool also uses internal Builder interfaces, so host compatibility matters.
2. Include laser-scan data and the corresponding clipping or spraying objects in the input sessions, with aligned coordinates.
3. Adapt [UPVBPluginLaserscanClipping.xml](UPVBPluginLaserscanClipping/UPVBPluginLaserscanClipping/UPVBPluginLaserscanClipping.xml), placing it beside the first session or in the installed plugin/helper directory.
4. On clipping objects, supply a `LaserscanClipping` attribute with the intended mode below. The older `LaserscanClippingMode` attribute is accepted as a fallback.
5. Export to a separate output and inspect boundaries, textures and isolated objects.

| Mode | Intended operation |
| --- | --- |
| `ClipOutside` | Trim the scan outside the clipping region. |
| `Delete` | Cut out scan geometry inside the region. |
| `Isolate` | Extract the region into separate geometry. |
| `ignore` | Skip the clipping object. |
| `Update` | Use the old/new scan update workflow. |

Legacy values `OuterBound`, `Cut` and `Replace` correspond to `ClipOutside`, `Delete` and `Isolate`. Spraying objects use `LaserscanUid` and `ElementUid` plus geometry in the `Spraying` aspect. Objects carrying an `Objects` attribute are ignored by mode selection.

## Main settings

- `RetainTextureClipping` and `RetainTextureSpraying` control texture retention.
- `DeleteUnmatchedSpraying` controls removal of unmatched spraying data.
- `MergeClippedItemsByName` groups clipped results by name.
- `DuplicateIsolateMeshes` controls duplication for isolation.
- `CreatePanoramaPOI` adds panorama markers in `PointOfInterest`.
- `ClipPanorama` and `ClipPanoramaDistance` control panorama clipping.
- `UpdateOldUPVC` and `UpdateNewUPVC` identify the old/new scan inputs for updates.
- `ThreadCount` controls concurrency; `LegacyScanSupport` enables legacy scan handling.

The XML sample enables several optional operations. Review it before use. If nothing changes, check the log, attribute spelling, scan format and clipping geometry. The plugin consumes prepared clipping data; it does not supply an interactive clipping editor.

[All solutions](UniversalPlantViewer.SDK.md)

---

# UDiTH SDK — UPVBPluginMapping

# Mapping — transform attributes with rules

Applies configurable rules to 3D or IntelliPID objects during export. Use it to assign standard values, rename or remove attributes, or remove selected objects.

## Use

1. Install the plugin and its dependencies in a compatible Builder.
2. Create `UPVBPluginMapping.json` beside the first input session or in the installed plugin/helper directory.
3. Define 3D rules under `Mappings` and drawing rules under `MappingsIntelliPid`.
4. Export and check each rule's affected-element count in the log, then inspect representative output objects.

This example adds `Discipline=Piping` to objects already marked `Task=Piping`:

```json
{
  "ProcessOrder": 1,
  "Debug": false,
  "Mappings": [{
    "Name": "Classify piping",
    "ConditionCombineMode": "AND",
    "Conditions": [{"Type": "Text", "Key": "Task", "Value": "Piping"}],
    "Actions": [{"Type": "Set", "Key": "Discipline", "Value": "Piping"}]
  }],
  "MappingsIntelliPid": []
}
```

## Rule options

Conditions support `Text`, `Regex`, `Exist` and `NotExist`, combined using `AND` or `OR`. Actions support `Set`, `Delete`, `Rename`, `DeleteElement` and `RenameIdentifier`. For an attribute rename, `OldKey` identifies the source and `Key` the destination. Advanced mappings include inline value tables, regular expressions, functions, hashes and math; have those expressions reviewed for your specific data.

Rules run in sequence, so later rules can depend on earlier changes. Additional `.json` configurations in an `UPVBPluginMapping` folder beside the first session are appended. Avoid relying on the order of those separate files.

## Checks

`DeleteElement` removes objects from the export, and renaming identifiers can affect downstream references. Test those rules in a separate output. If no objects change, check attribute names, conditions and the loaded configuration path. An XML file with the same base name takes precedence over JSON in the same search directory.

[All solutions](UniversalPlantViewer.SDK.md)

---

# UDiTH SDK — UPVBPluginRenameAttributes

# Rename Attributes — standardize attribute names

Renames attributes throughout a model using an Excel mapping workbook, with separate mappings for 3D and IntelliPID data.

## Use

1. Install the plugin and place [UPVBPluginRenameAttributes.xml](UPVBPluginRenameAttributes/UPVBPluginRenameAttributes/UPVBPluginRenameAttributes.xml) beside it.
2. Set `ConnectionString` to the workbook path and set `SheetName3D` / `SheetNamePID` to the sheets to process. Leave a sheet-name setting empty to skip that data type.
3. In each sheet, use the exact headers `Original Name` and `New Name`:

| Original Name | New Name |
| --- | --- |
| EquipmentTag | Equipment Tag |
| SystemPath | System Path |

4. Optionally place `RenameAttributes.xlsx` beside the first input session; it overrides the configured workbook path.
5. Export and inspect attribute names on representative objects.

## Checks

These are global attribute-name changes, not per-object value replacements. Update downstream searches, mappings or reports that depend on the old names. Use `ProcessOrder` to coordinate with other plugins.

Blank mappings are skipped. Missing original attributes generate warnings. Conflicts with an existing destination name may produce errors; inspect the log rather than assuming a merge. If nothing is renamed, verify the workbook path, sheet names and exact column headers.

[All solutions](UniversalPlantViewer.SDK.md)

---

# UDiTH Portal — Overview

# UDiTH Portal Overview

UDiTH Portal is a server-based centralised platform that contains multiple optional submodules:

### Central Model Repository

Centrally manage and version UDiTH models. Models are imported into the repository and made available across your organisation. Each model can have multiple versions, and storage is fully managed by Portal.

### Search (Tag Register)

Extract model attributes into a searchable tag register. This allows users to find model objects, both 3D and PID, across multiple models through Portal search.

### Model Import

Import models into the Central Model Repository, either manually through the web interface or automatically by placing files in an import folder. Automated imports can be configured through a `modelDefinition.json` file.

### Browser-Based Viewing

View UDiTH models in a lightweight browser. Rendering is carried out on a separate machine. This requires render servers and TURN servers.

## Architecture Overview
![](media/Portal_traffic.png)

The solution consists of multiple components. The image above contains a reference architecture. This is only one possible setup; the product is very flexible and can be deployed in many different configurations to suit your environment. Clients connect to a DMZ that hosts the Portal/MSSQL access server, the ICE server, and the Keycloak authentication server, while the render servers are placed in the internal network.

The components are split across two network zones, both of which may be hosted in the cloud or on-premises:

- **DMZ** — the client-facing zone. It hosts the Portal access server (UDiTH Portal / MSSQL connection and access portal), the ICE server (CAXturn on Windows or Linux, or coturn on Linux), and the Keycloak authentication server. Clients from all networks reach these components directly.
- **Internal Network** — hosts the render servers (Windows). Render servers are not exposed to clients directly. They connect outwards to the Portal for management, to the ICE server for relayed streams, and provide the direct (peer-to-peer) stream to clients where possible.

The diagram also distinguishes the types of traffic that flow between these components:

| Traffic       | Description                                                                 |
|---------------|-----------------------------------------------------------------------------|
| Request       | The initial client request to the Portal access server.                     |
| Direct Stream | The peer-to-peer WebRTC stream from a render server to the client.          |
| Relay Stream  | The WebRTC stream relayed through the ICE server when no direct (peer-to-peer) connection can be established. |
| Authentication | Client and component authentication handled by Keycloak.                   |
| Management    | Control traffic between the Portal and the render servers.                  |

Keycloak is used to handle user authentication and authorisation and can be extended to integrate with different OpenID Connect authority providers.

An MSSQL database handles the persistence layer. It can be hosted on the same server or on an additional server.

The ICE server is used as a proxy server for WebRTC connections between the render servers and clients if no peer-to-peer connection can be established.

CAXperts provides the deployment files. The operational aspects and infrastructure must be handled by the customer.

Portal, ICE, Keycloak, and SQL Server are often combined, and this is an approved hosting setup.

## Requirements

For component requirements, including ports, supported versions, and database notes, see the [Requirements](/UDiTH%20Portal/Requirements) page.

## Licensing

UDiTH Portal is licensed.

Please contact sales if you are interested.

Licence operation requires an active internet connection to our licensing server.

---

# UDiTH Portal — Migration Guide

# Migration Guide

This guide walks you through upgrading from UPV Webservices to the new UDiTH Portal. Alternatively, you can set up a fresh instance.

## Breaking changes

> ⚠️ **Important**: Review these changes before proceeding with your migration.

### URL structure

- **Model URLs have changed** - BBV includes basic compatibility, but you should update to the new URLs as soon as possible.
- **Portal default path** has changed from `/UpvWebservices` to `/Portal`.
  - On Linux, `/` is used by default.
  - This is configurable and can be customised during installation.

### Model Management

- **Import requirement**: All models must go through the new import procedure in order to be compatible with BBV and Streaming.
- **Read-only imports**: Imported models cannot be modified after import.
  - If you need frequent file updates, wait for a future release that supports individual file updates.
- **New format required**: Models must be packaged as ZIP files with the required folder structure.

### Permissions & Database

- **Unified permissions**: There are currently no separate permissions for BBV and Streaming. This is planned to change in a future version.
- **Database requirements**: The Tag Register functionality requires SQL Server. The storage limits in SQL Express may not be sufficient.

### Model Format Requirements

When preparing models for import:

- **Create ZIP archives** from your model folders.
- **Ensure the correct structure**: The `Data` folder must be at the top level of the ZIP file.
- **Validate the structure** before importing to avoid errors.

### MSSQL

- When using the new Tag Register functionality, SQL Express is no longer sufficient because of the storage limitations in Express.

### Installer

- The installer no longer validates prerequisites. Ensure that all prerequisites are met before installation.

## Backups

Ensure that backups of the database and models are created before performing any migration.

## Migration

1. **Verify model accessibility** on the Portal server.
2. **Document all secrets** and save `sharedsettings.json`.
3. **Create comprehensive backups** of the database and models.
4. **Uninstall the web server** and restart the server.
5. **Stop and uninstall the render server**.
6. **Install Portal** in the new installation path and enter the required secrets during setup.
7. **Restart IIS** and verify that Portal is accessible.
8. **Install the new UDiTH render server** and configure it against the **new** Portal instance.
   - Note: models no longer need to be stored on the render server.
9. **Copy the model folders** to the `Models` directory of the new installation.
10. **Use the Migration tab** in the Portal web interface:
    - Navigate to **Central Model Repository → Migration**.
    - Convert the models to the new format and copy them to the import folder.

    ![Model Migration](media/Portal_Migration.png)

11. **Import the models** via the **Model import** section. Create new models and assign model IDs and version IDs.
12. **Configure permissions** for the newly imported models.
13. **Import future models** directly in the new format by using the import folder.

---

# UDiTH Portal — Requirements

# Requirements

This page consolidates the requirements for all UDiTH Portal components. Please review the sections relevant to the components you intend to deploy.

Each component has its own section: [Portal](#portal), [Render server](#render-server), [Keycloak](#keycloak), [ICE server (CAXturn)](#ice-server-caxturn) and [Databases](#databases). The [Ports](#ports) and [Common requirements](#common-requirements) sections apply to all of them.

## Ports

For communication between the individual components and the clients, certain ports must be available. The port values shown are examples and can be adjusted.

| Service        | Protocol/Port                                  |
|--------------|----------------------------------------------|
| Request       | 443/TCP                                      |
| Direct Stream | P2P                                          |
| Authentication | 443/TCP or 8443/TCP (if on the same server with Windows) |
| Relay Stream  | 3478 TCP/UDP, port range 50000-51000/UDP (2 ports per stream, but most WebRTC implementations in browsers can open more than 2, in some cases 4-6. This also depends on the number of TURN server addresses specified in the configuration.) |
| Management    | 443/TCP                                      |

## Common requirements

The following requirements apply to every server, regardless of component or platform.

- **Accurate server time** configuration (using NTP is recommended).
- **Trusted SSL certificate** for the alias or (sub)domain:
  - Must meet current security standards.
  - The certificate and key are required as a `.pfx` file or similar.
- **Alias or subdomain** for the server.

### Network

- **Recommended:** 10 Gbit network connection
- **Minimum:** 1 Gbit network connection

### Latest validated third-party versions

- Keycloak 26.7.2
- MSSQL 2025

## Portal

### Operating systems

Any x64 Linux system that supports Docker containers, or Windows Server 2019 / 2022 / 2025.

### CPU and memory

- **8-16 CPU cores**
- **8-16 GB RAM**

Very large installations can require more. Large model collections, a high
number of concurrent users, or attribute indexing all increase the demand.

### Storage

The backing storage for the Central Model Repository must be fast enough to saturate the network connection when serving models. As a guideline, plan for approximately **1000 read IOPS** and **100 MB/s** of read throughput per **1 Gbit** of network connection.

| Network connection | Read IOPS   | Read throughput |
|--------------------|-------------|-----------------|
| 1 Gbit             | 1000 IOPS   | 100 MB/s        |
| 10 Gbit            | 10000 IOPS  | 1000 MB/s       |

Individual **hard disk drives (HDDs) are not supported** for the Central Model
Repository. A single HDD delivers roughly 75-200 random read IOPS and therefore
cannot meet even the 1 Gbit guideline above. Use SSD/NVMe storage, or a storage
system (SAN/NAS/RAID array) that demonstrably reaches the values in the table.

### Software (Windows)

- **.NET 10 Hosting Bundle**.
- **SQL Server Express**
- **IIS installation** with the **Default Website** accessible via **HTTPS**:
  - Setup via HTTP is **not supported**.
- **Keycloak 26.x Server** must be pre-installed.

### Software (Linux)

- **Docker** is required.
- A **reverse proxy** in front of Portal is recommended.
- **Keycloak 26.x Server**.

## Render server

### Operating systems

Windows Server 2019 / 2022 / 2025, or Windows 11.

### CPU and memory

Per stream:

- **2 CPU cores**
- **4 GB RAM**

Minimum **8 CPU cores** per server. These are recommended requirements. Depending on the model size, the requirements may be lower or higher. Please verify this with your own models. We do not recommend going above 40 streams per server.

### GPU

- **2 GB VRAM** per stream.
- NVIDIA GPUs such as **A4500 ADA** or **RTX PRO 4500 Blackwell/Server** are recommended.
- **AMD GPUs** are not officially supported.
- Current GPU drivers installed.

### Other

- The **render server** must have a **logged-in user session**:
  - The screen can be locked, but a user must remain logged in.

## Keycloak

### Operating systems

Any x64 Linux system that supports Docker containers, or Windows Server 2019 / 2022 / 2025.

### Other

- Ensure that Keycloak can either:
  - Run on a different port than 443 (used by the web service), **or**
  - Use a separate IP address/server.
- Currently, it seems **not possible** to use a reverse proxy like IIS to host both Keycloak and the web service on the **same subdomain and port**.

## ICE server (CAXturn)

A **TURN/STUN server** may be required for BBV, depending on your local network.

### Operating systems

Any x64 Linux system that supports Docker containers, or Windows Server 2019 / 2022 / 2025.

## Databases

- **Microsoft SQL Server 2019 or later**:
  - A database must already be created.
  - A user with **DB Owner** permissions on that database is required.
- To use the attribute indexing feature, a non-Express version of SQL Server should be used because of the storage requirements.

All database operations must be handled by the customer. Regular backups are recommended.

Direct data manipulation on the database is prone to error and should be avoided.

---

# UDiTH Portal — Setup Keycloak

# Setup Keycloak

UDiTH Portal now uses Keycloak as an identity provider.

Keycloak is an open-source software product that provides single sign-on together with identity and access management for modern applications and services.

This document is intended as a quick reference for setting up a basic Keycloak instance in practice. For a more detailed explanation of Keycloak features, please refer to the official documentation at <https://www.keycloak.org/guides>.

If you already have a running Keycloak instance, it can also be used.
The UDiTH Portal setup creates a new realm in the selected Keycloak instance with the basic configuration required to get started. The realm name is configurable during installation.

## Requirements

See the [Requirements](/UDiTH%20Portal/Requirements) page for the Keycloak requirements.

## Install prerequisites

### Download Keycloak

UDiTH Portal supports Keycloak version `26.7.2`.

For example, you can download it here:
<https://github.com/keycloak/keycloak/releases/download/26.7.2/keycloak-26.7.2.zip>

Extract the archive.

### Install Java JDK

Java JDK/JRE or OpenJDK/JRE is required, using LTS 21 or LTS 25.

Alternative versions such as Corretto (Amazon), Adoptium (Eclipse) or Microsoft and others also work.

#### OpenJDK installation

Whichever option you choose for installing Java, OpenJDK, Corretto, or Adoptium, ensure that the `JAVA_HOME` environment variable is set and points to the installation folder.

## Keycloak configuration

Navigate to your Keycloak file directory and open `.../keycloak/conf/keycloak.conf`

The settings found here are used when starting Keycloak. Lines starting with a `#` are ignored.

> **Note:** The default `keycloak.conf` that ships with the Keycloak package contains a large number of commented-out settings using different key names than those shown in this guide. This may cause confusion when comparing the file to the examples below. If you prefer a clean starting point, you can delete the contents of `keycloak.conf` and add only the settings you need.

### Database options

Keycloak supports multiple database providers.

The example below shows the setup for MSSQL:

#### MSSQL Server
```
db=mssql
db-username=username
db-password=pw
db-url=jdbc:sqlserver://hostname:port;databaseName=database;encrypt=true;trustServerCertificate=true;

transaction-xa-enabled=false
```

To use MS SQL, first create a new user and a new database schema in SQL Server. Ensure that the user has administrative access to the database and is allowed to create tables in that schema.

In SQL Server Management Studio, open the login properties for the user, go to **User Mapping**, select the Keycloak database, and check **db_owner** under database role membership.

![MSSQL DB Owner](media/Keycloak_MSSQL_DBOwner.png)

Please ensure that MSSQL is configured to listen:

`SQL Server Configuration Manager -> SQL Server Network Configuration -> <ServerName> -> TCP/IP = Enabled`

![Enable TCP/IP](media/Keycloak_MSSQL_EnableTCPIP.png)

`Properties -> IP Addresses -> IPAll -> TCP Port = 1433`

![Set TCP Port](media/Keycloak_MSSQL_SetPort.png)

### Production configuration

For production use, some additional configuration is required.

Extended documentation is available on the Keycloak website:
<https://www.keycloak.org/server/configuration-production>

#### Set up SSL certificates

<https://www.keycloak.org/server/enabletls>

##### File based approach

Keycloak supports different certificate formats. In this setup, the following is used:
- a `.pfx` file containing the certificate and the key

In `.../keycloak/conf/keycloak.conf`, specify the path to your PFX file and the password used when exporting the `.pfx` file.

```
https-key-store-file=${kc.home.dir}/conf/certificate.pfx
https-key-store-password=password
```
This is a relative path based on the Keycloak base directory.

You therefore need to copy the certificate file there. Absolute paths to a different location are also possible.

##### Others

The official documentation also describes several other ways to provide certificate information to Keycloak.

See <https://www.keycloak.org/server/enabletls> for the alternatives that best fit your use case.

##### Set up hostname

Set the hostname at which the instance can be reached:
```
hostname=https://<server>:<port>
```
For security reasons, the general Keycloak admin UI can be hosted separately. If this is not required, you can use the same URL here:
```
hostname-admin=https://<server>:<port>
```

If you access the server via port `443`, remove the port number from the URLs, as this can otherwise cause issues.

##### Set cache mode

`.../keycloak/conf/keycloak.conf`
```
cache=local
```

##### Add ingoing firewall rule for port

Allow incoming traffic on the port on which Keycloak is running.

### Start

Open a command line with administrator permissions in `.../keycloak/bin`.
`kc.bat start`

Building the server may take some time. After that, Keycloak should be available on the specified port.

### Security

See the [Secure Setup Guidelines](/UDiTH%20Portal/Secure%20Setup%20Guidelines) page.

---

# UDiTH Portal — Setup Portal Window

# Setup Portal

## Requirements

See the **Portal** section on the [Requirements](/UDiTH%20Portal/Requirements) page, including **Software (Windows)**.

## Installation

We recommend using a guided installation with CAXperts for UDiTH Portal. Please contact support@caxperts.com for information.

Before starting the installation, ensure that all requirements are fulfilled and all prerequisites are installed.

![Start](media/PortalInstallation_01_start.png)

To continue with the installation, accept the EULA and click **Next**.

![EULA](media/PortalInstallation_02_eula.png)

Enter the Keycloak information, such as the URL, admin account, and password. The realm name can be chosen according to your preference.

![Keycloak](media/PortalInstallation_03_keycloak.png)

Choose a suitable installation path and click **Next**.

![InstallPath](media/PortalInstallation_04_installpath.png)

Enter the licence key provided to you, together with a client description, and click **Next**.

![License](media/PortalInstallation_05_license.png)

Select which IIS website to use.

![IISWebsite](media/PortalInstallation_06_IISWebsite.png)

The only setting that needs to be changed is the **Hostname**. This should be `https://<alias/(sub)domain>/Portal`.
Ensure that this matches the certificate.

![IISWebsiteSettings](media/PortalInstallation_07_IISWebsiteSettings.png)

Configure the STUN/TURN server that you want to use. You can leave the default setting if BBV will not be used.

![STUN/TURN Setup](media/PortalInstallation_08_turn.png)

Enter the details of the MSSQL database. An `sa` user is not required. You only need a database together with a database owner user for the database that is to be used.

![Database Login](media/PortalInstallation_09_database.png)

Please specify the location of the folders to be used for the Central Model Repository:

- **Import folder** — Temporary location where model ZIP files are placed for import. Files are removed from this folder after a successful import.
- **Storage folder** — Permanent location where imported models are stored and served from. This folder grows over time and should have sufficient disk space.

![CMR Model Folder](media/PortalInstallation_10_cmr.png)

Click **Install** to start the installation.

![Ready to install](media/PortalInstallation_11_readytoinstall.png)

## Accessing Portal

Go to the Keycloak installation and update the `defaultadmin` password in the realm you specified.
Then go to `https://<alias/(sub)domain>/Portal` and sign in by using `defaultadmin` and the password you defined.

You can add additional users through the web interface or connect the portal to an SSO provider.

![Keycloak Login](media/Portal_Login.png)

## Configuration

On Windows, Portal settings are stored in the `Settings/sharedsettings.json` file inside the installation directory.

For the complete list of available settings, and details on how to provide them, see the [Portal Configuration](/UDiTH%20Portal/Portal%20Configuration) page.

## Security

See the [Secure Setup Guidelines](/UDiTH%20Portal/Secure%20Setup%20Guidelines) page.

---

# UDiTH Portal — Setup Portal Linux

# Setup Portal Linux

## Requirements

See the **Portal** section on the [Requirements](/UDiTH%20Portal/Requirements) page, including **Software (Linux)**.

## Installation

We recommend using a guided installation with CAXperts for UDiTH Portal. Please contact `support@caxperts.com` for information.

The Docker images are available at `quay.io/caxperts/portal` and `quay.io/caxperts/caxturn`.

### Reverse Proxy

Please use a reverse proxy in front of Portal. See the [Secure Setup Guidelines](/UDiTH%20Portal/Secure%20Setup%20Guidelines) page.

### Installation Steps

- Ensure that all requirements are met.
- Ensure that MSSQL Server and Keycloak are accessible from the new Portal container.
- Create a container for Portal. A template `docker-compose.yml` file with the most important settings is included below.
  - In comparison with the Windows version, the Docker variant mainly uses environment variables to configure the instance.

### Sample `docker-compose.yml` File

```yml
services:
  portal_demo:
    image: quay.io/caxperts/portal:${PORTAL_VERSION:-latest}
    restart: unless-stopped
    environment:
      - KeycloakAdmin__Name=${KEYCLOAK_ADMIN_USER}
      - KeycloakAdmin__Password_file=${KEYCLOAK_ADMIN_PASSWORD}
      - KeycloakUrl=https://${KEYCLOAK_DOMAIN}${KEYCLOAK_PATH:-/}
      - KeycloakRealm=${KEYCLOAK_REALM}
      - ConnectionStrings__AdminConnection=${PORTAL_CONNECTION_STRING}
      - ServerUrl=https://${PORTAL_DOMAIN}
      - LicenseKey_file=${PORTAL_LICENSEKEY}
    networks:
      - sqlserver
      - external
    volumes:
      - ${PORTAL_IMPORT_FOLDER}:/import
      - ${PORTAL_STORAGE_FOLDER}:/storage
      - ${PORTAL_UPLOAD_FOLDER}:/upload
networks:
  external:
    external: true
    name: external
  sqlserver:
    external: true
    name: sqlserver
```

### Sample `docker-compose.yml` File with CAX Turn for BBV

```yml
services:
  portal_demo:
    image: quay.io/caxperts/portal:${PORTAL_VERSION:-latest}
    restart: unless-stopped
    environment:
      - KeycloakAdmin__Name=${KEYCLOAK_ADMIN_USER}
      - KeycloakAdmin__Password_file=${KEYCLOAK_ADMIN_PASSWORD}
      - KeycloakUrl=https://${KEYCLOAK_DOMAIN}${KEYCLOAK_PATH:-/}
      - KeycloakRealm=${KEYCLOAK_REALM}
      - ConnectionStrings__AdminConnection=${PORTAL_CONNECTION_STRING}
      - ServerUrl=https://${PORTAL_DOMAIN}
      - StreamingConfig__IceConfig__IceServers__0__Urls__0=turn:${TURNSERVER_ADDRESS}:${TURNSERVER_UDP_PORT}
      - StreamingConfig__IceConfig__IceServers__0__CredentialType=password
      - StreamingConfig__IceConfig__IceServers__0__Username=${TURNSERVER_USERNAME}
      - StreamingConfig__IceConfig__IceServers__0__Credential=${TURNSERVER_PASSWORD}
      - LicenseKey_file=${PORTAL_LICENSEKEY}
    networks:
      - sqlserver
      - external
    volumes:
      - ${PORTAL_IMPORT_FOLDER}:/import
      - ${PORTAL_STORAGE_FOLDER}:/storage
      - ${PORTAL_UPLOAD_FOLDER}:/upload
  turn:
    image: quay.io/caxperts/caxturn:${CAXTURN_VERSION:-latest}
    # Port range mapping is very slow, so network_mode: host is used.
    network_mode: host
    environment:
      - CAXTURN_ENABLE_UDP=true
      - CAXTURN_ENABLE_TCP=true
      - CAXTURN_UDP_PORT=${CAXTURN_UDP_PORT}
      - CAXTURN_TCP_PORT=${CAXTURN_TCP_PORT}

      - CAXTURN_MIN_PORT=${CAXTURN_MIN_PORT}
      - CAXTURN_MAX_PORT=${CAXTURN_MAX_PORT}

      - CAXTURN_PUBLIC_IP=${CAXTURN_PUBLIC_IP}
      - CAXTURN_USERNAME=${CAXTURN_USERNAME}
      - CAXTURN_PASSWORD=${CAXTURN_PASSWORD}
    restart: unless-stopped
networks:
  external:
    external: true
    name: external
  sqlserver:
    external: true
    name: sqlserver
```

### Sample `docker-compose.yml` for Keycloak

```yml
services:
  keycloak:
    image: quay.io/keycloak/keycloak:${KEYCLOAK_VERSION:-26.7.2}
    restart: unless-stopped
    environment:
      # >= 26
      KC_BOOTSTRAP_ADMIN_USERNAME: ${KEYCLOAK_ADMIN_USER}
      KC_BOOTSTRAP_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
      # <26
      KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN_USER}
      KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
      KC_DB: mssql
      KC_DB_URL: "jdbc:sqlserver://${KEYCLOAK_SQLSERVER_ADDRESS:-mssql:1433};databaseName=${KEYCLOAK_SQLSERVER_DATABASENAME};encrypt=true;trustServerCertificate=true;"
      KC_DB_USERNAME: ${KEYCLOAK_SQLSERVER_USER}
      KC_DB_PASSWORD: ${KEYCLOAK_SQLSERVER_PASSWORD}
      KC_HEALTH_ENABLED: "true"
      KC_METRICS_ENABLED: "true"
      KC_HTTP_ENABLED: true
      KC_PROXY_HEADERS: xforwarded
      KC_HOSTNAME: https://${KEYCLOAK_DOMAIN}${KEYCLOAK_PATH:-/}
      KC_HTTP_RELATIVE_PATH: ${KEYCLOAK_RELATIVE_PATH:-/}
    command: start
    networks:
      - "sqlserver"
      - "external"
    healthcheck:
      test: ['CMD-SHELL', '[ -f /tmp/HealthCheck.java ] || echo "public class HealthCheck { public static void main(String[] args) throws java.lang.Throwable { System.exit(java.net.HttpURLConnection.HTTP_OK == ((java.net.HttpURLConnection)new java.net.URL(args[0]).openConnection()).getResponseCode() ? 0 : 1); } }" > /tmp/HealthCheck.java && java /tmp/HealthCheck.java http://localhost:9000${KEYCLOAK_RELATIVE_PATH:-/}/health/live']
      interval: 5s
      timeout: 3s
      retries: 3
      start_period: 1m
    volumes: 
      - /root/storage/keycloak/providers:/opt/keycloak/providers
networks:
  external:
    external: true
    name: "external"
  sqlserver:
    external: true
    name: "sqlserver"
```

### Sample `docker-compose.yml` for MSSQL

```yml
services:
 mssql:
    image: mcr.microsoft.com/mssql/server:${MSSQL_VERSION:-2025-latest}
    environment:
      SA_PASSWORD: ${SQLSERVER_SA_PASSWORD}
      ACCEPT_EULA: "Y"
    volumes:
      - ${SQLSERVER_STORAGE_FOLDER}:/var/opt/mssql
    ports:
      - 1433:1433 # Not required, but useful for remote connections. Secure this appropriately.
    restart: unless-stopped
    healthcheck:
      # -C disable checks for encryption
      test: /opt/mssql-tools18/bin/sqlcmd -S localhost -C -U sa -P "$SQLSERVER_SA_PASSWORD" -Q "SELECT 1" -b -o /dev/null
      interval: 5s
      timeout: 3s
      retries: 3
      start_period: 10m
networks:
  default:
    external: true
    name: sqlserver
```

### Image Variants and Volume Permissions

The `quay.io/caxperts/portal` image is published with several base-OS variants.
These variants do **not** all run as the same user, so the host directories that
are mounted as volumes (`/import`, `/storage` and `/upload`) must be readable and
writable by the user of the variant you use.

| Tag               | Base OS                          | Runs as        | Required ownership of the host folders |
| ----------------- | -------------------------------- | -------------- | -------------------------------------- |
| `ubuntu`          | Ubuntu 26.04 LTS                 | `root` (uid 0) | No change required                     |
| `ubuntu-chiseled` | Ubuntu 26.04 LTS, chiseled       | uid `1654`     | `chown -R 1654 <folder>`               |
| `alpine`          | Alpine Linux                     | `root` (uid 0) | No change required                     |
| `ubi`             | RHEL 9 (UBI)                     | uid `1001`     | `chown -R 1001 <folder>`               |
| `noble`           | Ubuntu 24.04 LTS *(legacy)*      | `root` (uid 0) | No change required                     |
| `noble-chiseled`  | Ubuntu 24.04 LTS, chiseled *(legacy)* | uid `1654` | `chown -R 1654 <folder>`             |

The `noble` variants are kept for compatibility and will be removed with release
2027.0. New installations should use `ubuntu` or `ubuntu-chiseled`.

The uids of the non-root variants do not need to exist on the host; the numeric
owner is what matters.

All three volumes require **read, write and delete** access:

| Mount      | Purpose                                             |
| ---------- | --------------------------------------------------- |
| `/storage` | Converted models, caches and temporary data          |
| `/import`  | Files picked up for import                           |
| `/upload`  | Files uploaded through the web interface             |

Because deleting a file on Linux is a permission of the *containing directory*,
the container user needs **write and execute** permissions on the directories
themselves, not only on the files inside them.

The image already contains the three directories, owned by the application user
with group `root` and group write permission, and declares them as volumes. A
named or anonymous Docker volume mounted over one of these paths inherits that
ownership, so it needs no preparation. A **bind mount** keeps the ownership of
the host directory and must therefore be prepared as described above.

Example for the `ubuntu-chiseled` variant:

```bash
mkdir -p /srv/udith/storage /srv/udith/import /srv/udith/upload
chown -R 1654:1654 /srv/udith/storage /srv/udith/import /srv/udith/upload
chmod -R u+rwX /srv/udith/storage /srv/udith/import /srv/udith/upload
```

Notes:

- The root-based variants (`ubuntu`, `noble`, `alpine`) write files owned by
  `root` on the host. If you later switch to `ubuntu-chiseled`, `noble-chiseled`
  or `ubi`, the existing content of all three volumes must be re-owned to the new
  uid.
- Network shares (NFS/CIFS) usually ignore `chown`. Mount them with the
  matching `uid=`/`gid=` options instead (for example
  `uid=1654,gid=1654,file_mode=0660,dir_mode=0770` for CIFS). The share must also
  permit deleting files.

### CAXturn Image Variants

The `quay.io/caxperts/caxturn` image is published in three variants. All of them
run as uid `65532` and need no volumes, so the choice only affects what else is
present in the image.

| Tag       | Base OS      | Notes                                             |
| --------- | ------------ | ------------------------------------------------- |
| `scratch` | none         | Smallest image, contains only the server binary   |
| `debian`  | Debian       | Includes a shell for troubleshooting              |
| `alpine`  | Alpine Linux | Includes a shell, smaller than the Debian variant |

## Accessing Portal

Go to the Keycloak installation and update the `defaultadmin` password in the realm you specified.

Then go to `https://<alias/(sub)domain>/Portal` and sign in by using `defaultadmin` and the password you defined.

You can add additional users through the web interface or connect the portal to an SSO provider.

![Keycloak Login](media/Portal_Login.png)

## Configuration

In comparison with the Windows version, the Docker variant is configured mainly through environment variables, optionally backed by Docker secret files.

For the complete list of available settings, and details on how to provide them on both Linux and Windows, see the [Portal Configuration](/UDiTH%20Portal/Portal%20Configuration) page.

## Security

See the [Secure Setup Guidelines](/UDiTH%20Portal/Secure%20Setup%20Guidelines) page.

---

# UDiTH Portal — Portal Configuration

# Portal Configuration

This page describes how UDiTH Portal is configured and lists all available settings.

Portal uses the standard .NET configuration system. Throughout this page, settings are written using the .NET hierarchical notation, with a colon (`:`) separating each level of the hierarchy, for example `KeycloakAdmin:Name`. Array elements are addressed by their index, starting at `0`, for example `Serilog:WriteTo:0:Name`. Where a setting represents an array, the index is shown as `[]`.

## Loading Configuration

How a setting is provided depends on the platform.

### Linux (Docker)

On Linux, Portal is configured through environment variables, optionally backed by Docker secret files.

#### Environment variables

Each setting is provided as an environment variable. Because an environment variable name cannot contain a colon, replace every `:` in the setting name with a double underscore (`__`).

For example, the setting `KeycloakAdmin:Name` becomes:

```env
KeycloakAdmin__Name=admin
```

And an array element such as `Serilog:WriteTo:0:Name` becomes:

```env
Serilog__WriteTo__0__Name=Console
```

#### Docker secret files

Any variable can also be read from a file, similar to Docker secrets. To do this, append the `_file` suffix to the variable name and reference the file that contains the secret.

```env
KeycloakAdmin__Password=<pw>
KeycloakAdmin__Password_file=/run/secrets/keycloak_password
```

#### Escaping `$` in Docker Compose

In Docker Compose, a value containing `$` is interpreted as a variable reference. A literal `$` must therefore be escaped as `$$`.

### Windows

On Windows, settings are stored in the `Settings/sharedsettings.json` file inside the Portal installation directory. The configuration uses standard JSON, with the hierarchy expressed as nested objects. For example, the setting `KeycloakAdmin:Name` is written as:

```json
{
  "KeycloakAdmin": {
    "Name": "admin"
  }
}
```

Array elements become JSON arrays:

```json
{
  "Serilog": {
    "WriteTo": [
      { "Name": "Console" }
    ]
  }
}
```

## Settings Reference

The following list contains all available settings, written in .NET (`:`) notation. Remember to translate each name to the form required by your platform, as described under [Loading Configuration](#loading-configuration).

```env
###############################################################################
# Keycloak
###############################################################################

# URL used to connect to Keycloak. This is the public URL that both the server
# and browser clients use for authentication (e.g. https://keycloak.example.com).
KeycloakUrl=https://...
# Keycloak realm to use for authentication. This realm is created automatically
# during initial setup if it does not exist yet.
KeycloakRealm=portal
# Keycloak admin user name. Needs permission to manage the realm specified above.
KeycloakAdmin:Name=admin
# Keycloak admin user password.
KeycloakAdmin:Password=...

###############################################################################
# Server
###############################################################################

# MSSQL database connection string. Must point to an existing database with a
# user that has DB Owner permissions.
# Example: Server=mssql;Database=portal;User=portaluser;Password=secret;TrustServerCertificate=true
ConnectionStrings:AdminConnection=...
# The public URL under which Portal is reachable by clients
# (e.g. https://portal.example.com/Portal).
ServerUrl=https://...
# If Portal is served behind a reverse proxy that changes the base path,
# set this to the new relative path (e.g. /Portal).
RelativePath=/ (default)
# The TCP port on which the Kestrel web server listens.
# Only relevant when running as standalone (RunAsStandAlone=true).
ServerPort=5000 (default)
# UDiTH licence key for automatic activation on startup.
# This can also be configured later through the web interface.
LicenseKey=...
# DANGEROUS: Accept invalid TLS certificates for outgoing connections to
# Keycloak and licensing servers. Use only in test environments.
IgnoreCertificateErrors=false (default)
# When enabled, user names are included in server logs for HTTP requests and
# BBV sessions. Disable in environments where logging personal data is restricted.
ShowPII=false (default)
# Additional domains for which CORS Access-Control-Allow-Origin headers are
# returned. Use one entry per domain. Required when embedding Portal in an
# external website or calling the API from a different origin.
AccessControlAllowOrigin:[]=...
# Maximum size in bytes for any HTTP request body accepted by the server
# (Kestrel limit). If tus uploads are enabled, TusSettings:MaxRequestBodySizeClient
# must be smaller than or equal to this value, otherwise Portal will not start.
MaxRequestBodySizeServer=31457280 (default, 30 MiB)
# Periodically synchronise Keycloak user accounts with Portal (interval in
# milliseconds). Set to 0 or a negative value to synchronise once at startup
# and then stop.
AccountRefreshInterval=600000 (default, 10 minutes)

###############################################################################
# Proxy
###############################################################################

# If a forward proxy is required for outgoing connections to Keycloak or the
# licensing server, configure it here.
Proxy:Address=...
Proxy:UserName=...
Proxy:Password=...

###############################################################################
# Standalone HTTPS
###############################################################################

# When running without a reverse proxy, or when fully encrypted connections are
# required, you can terminate TLS directly in Kestrel. Provide either a PFX
# file (with password) or a PEM key and certificate pair. For PEM, a password
# is only required if the private key is encrypted.
StandaloneHttpsSettings:PfxFile=...
StandaloneHttpsSettings:PemKeyFile=...
StandaloneHttpsSettings:PemCertFile=...
StandaloneHttpsSettings:Password=...

###############################################################################
# Model Import (Central Model Repository)
###############################################################################

# Directory that is scanned for new model files to import.
ModelImport:ImportDirectory=/import (default)
# Directory where imported and processed model files are stored permanently.
ModelImport:ProviderStoragePath=/storage (default)
# Interval in milliseconds at which the import directory is scanned for new files.
ModelImport:IntervalInMilliseconds=5000 (default)

###############################################################################
# Model Processing
###############################################################################

# Globally disable processing (attribute extraction) of imported models.
# Models will still be imported, but no attributes will be written to the database.
ModelProcessing:ForcedSkipProcessing=false (default)
# Force reprocessing of previously failed model files (e.g. those marked as
# InvalidInput) after the given time in milliseconds. Leave unset to disable.
ModelProcessing:ForcedReprocessingTime=...
# Maximum number of model versions kept per model. Older versions beyond this
# count are deleted automatically after a successful import. Leave unset to
# keep all versions. Can be overridden per model version in the
# Modeldefinition.json file or through the web interface.
ModelProcessing:MaxRetainedModelVersions=...

###############################################################################
# Model File Overwrites
###############################################################################

# Replace specific files inside every model during import. The key is the
# relative file path inside the model archive, the value is the path to the
# replacement file. Multiple entries can be added.
ModelFileOverwrites:Values:Data_default_upvf=/overwrittendefault.upvf
ModelFileOverwrites:Values:Data_appcontrols_json=/appcontrols.json

###############################################################################
# CMR Uploads
###############################################################################

# Enable the tus upload endpoint and the resumable upload client in the frontend.
# When enabled, large files are uploaded in chunks that can be resumed after
# interruptions.
TusSettings:TusEnabled=true (default)
# Directory where incomplete tus upload chunks are stored before processing.
TusSettings:StoreRootPath=... (default)
# Maximum size in bytes of a single upload chunk sent from client to server.
# Must be smaller than or equal to MaxRequestBodySizeServer, otherwise Portal
# will refuse to start.
TusSettings:MaxRequestBodySizeClient=20971520 (default, 20 MiB)
# Number of chunks uploaded in parallel from the client to the server.
# Values greater than 1 enable parallel chunk uploading, which can improve
# upload speed on high-bandwidth connections.
TusSettings:ParallelUploads=1 (default)
# Interval in milliseconds between cleanup runs that remove expired incomplete
# uploads from the tus storage directory.
TusSettings:CleanupInterval=900000 (default, 15 minutes)
# Time in milliseconds before an incomplete upload without activity is
# considered expired and eligible for cleanup.
TusSettings:SlidingExpiration=86400000 (default, 24 hours)
# Time in milliseconds, measured from the last received chunk, after which a
# fully uploaded file that is still present in the tus storage directory is
# treated as orphaned and deleted. Completed uploads are normally removed as
# soon as they have been handed over to the import directory; this is a safety
# net for the case where that hand-over failed.
TusSettings:CompletedFileRetention=3600000 (default, 1 hour)
# Time in milliseconds an upload may stay locked before the lock is considered
# abandoned and can be taken over by a new request. This prevents an upload
# from staying locked for the lifetime of the process when a client disconnects
# abruptly. The value must be larger than the time a single chunk
# (MaxRequestBodySizeClient) can take on a slow connection.
TusSettings:FileLockTimeout=180000 (default, 3 minutes)
# Number of completed uploads that are transferred into the model import
# directory at the same time. These are large sequential copies, so higher
# values usually reduce total throughput and increase peak disk usage.
TusSettings:MaxParallelTransfers=1 (default)
# Buffer size in bytes used when a completed upload has to be copied instead of
# moved, which is the case whenever the tus storage directory and the model
# import directory are on different volumes.
TusSettings:TransferBufferSizeBytes=1048576 (default, 1 MiB)
# Interval in milliseconds at which the frontend polls the upload status while
# the server finishes processing a completed upload.
TusSettings:StatusPollInterval=3000 (default)
# Time in milliseconds the frontend keeps waiting for a completed upload to
# finish processing before it stops polling and asks the user to check back
# later. This does not affect the upload itself.
TusSettings:StatusWaitTimeout=1800000 (default, 30 minutes)

###############################################################################
# Import Notifications (Webhook)
###############################################################################

# Portal can report the progress and the result of every model import to an
# external HTTPS endpoint. Every delivery carries an HMAC-SHA256 signature
# derived from the shared secret below. The feature is disabled while
# Notification:Webhook:Url is empty.
# The target can only be set through configuration; there is no web interface
# for it.

# Absolute HTTPS URL that receives the notifications. Plain HTTP and URLs that
# contain credentials are rejected on startup and disable the feature.
Notification:Webhook:Url=https://...
# Shared secret used to sign every delivery. Required as soon as a URL is set.
# Use at least 32 random bytes.
Notification:Webhook:Secret=...
# Timeout in milliseconds for a single delivery attempt.
Notification:Webhook:Timeout=10000 (default)
# Number of attempts per event before the event is dropped. Set to 1 to disable
# retries.
Notification:Webhook:MaxAttempts=5 (default)
# Number of consecutive failed deliveries after which notifications are
# suspended until Portal is restarted. Set to 0 to keep retrying indefinitely.
Notification:Webhook:SuspendAfterConsecutiveFailures=50 (default)
# Maximum number of pending deliveries held in memory. When the queue is full
# the oldest pending delivery is dropped and a warning is logged.
Notification:Webhook:QueueCapacity=1000 (default)
# Minimum time in milliseconds between two progress notifications for the same
# import. Step changes and the final result are always sent immediately.
Notification:Webhook:MinimumProgressInterval=5000 (default)
# Minimum change in progress (a fraction between 0 and 1) before a progress
# notification is sent.
Notification:Webhook:MinimumProgressDelta=0.05 (default)

###############################################################################
# Azure Key Vault
###############################################################################

# Any setting on this page can be read from an Azure Key Vault instead of being
# stored locally. A secret name maps to a setting name by replacing each colon
# with a double dash, for example the secret ConnectionStrings--AdminConnection
# provides the setting ConnectionStrings:AdminConnection.
# The Key Vault is only queried when a vault URI is configured.

# Absolute URI of the Key Vault.
AzureKeyVault:VaultUri=https://my-vault.vault.azure.net/
# Optional secret name prefix. When set, only secrets whose name starts with
# this prefix are read, and the prefix is removed from the resulting setting
# name. This allows one vault to serve several environments.
AzureKeyVault:Prefix=prod-
# Credentials used to authenticate against the vault. Leave unset to use the
# ambient Azure identity (e.g. a managed identity). Provide either a client
# secret or a client certificate.
AzureKeyVault:TenantID=...
AzureKeyVault:ClientID=...
AzureKeyVault:ClientSecret=...
AzureKeyVault:ClientCertificatePath=...

###############################################################################
# Frontend Customisation
###############################################################################

# Page title shown in the browser tab.
FrontendSettings:Title=UDiTH Portal (default)
# Show or hide the theme switcher in the user interface.
FrontendSettings:EnableThemeSelect=true (default)
# Theme applied when no user preference is set. Must be one of the values in
# AvailableThemes.
FrontendSettings:DefaultTheme=light (default)
# List of themes available for selection. Each entry is a theme name.
FrontendSettings:AvailableThemes:[]=light,dark
# Hide specific navigation menu items by key. Refer to the frontend source
# (nav-menu.definition.ts) for the list of available keys.
FrontendSettings:HiddenNavigation:[]=...
# URI scheme prefix used for the "Open locally" button. This is used to launch
# the local UDiTH application from the browser.
FrontendSettings:OpenLocallyPrefix=upvapi:// (default)
# When enabled, the external identifier and model version UID fields are
# read-only in the frontend and cannot be edited by users.
FrontendSettings:LockIdentifiers=false (default)

###############################################################################
# BBV / ICE Server
###############################################################################

# For BBV (Browser-Based Viewing), one or more ICE (STUN/TURN) servers must be
# configured so that WebRTC connections can be established between client and
# render server. Multiple ICE servers can be added by incrementing the array
# index.

# Authentication type for the ICE server. Currently only "password" is supported.
StreamingConfig:IceConfig:IceServers:[]:CredentialType=password (default)
# One or more STUN/TURN URLs. Most TURN servers require separate entries for
# UDP and TCP (e.g. turn:server:3478 and turn:server:3478?transport=tcp).
StreamingConfig:IceConfig:IceServers:[]:Urls:[]=turn:...
# Username for TURN server authentication.
StreamingConfig:IceConfig:IceServers:[]:Username=caxturn
# Password for TURN server authentication.
StreamingConfig:IceConfig:IceServers:[]:Credential=...

###############################################################################
# Logging (Serilog)
###############################################################################

# Serilog structured logging configuration. Logs can be written to console
# and/or files. Multiple sinks can be configured by incrementing the WriteTo
# array index. Forwarding logs to an OTLP endpoint does not need a sink here;
# it is configured under OpenTelemetry.

# Default minimum log level. Valid values: Verbose, Debug, Information, Warning,
# Error, Fatal.
Serilog:MinimumLevel:Default=Information
# Override log levels for specific namespaces to reduce noise.
Serilog:MinimumLevel:Override:Microsoft.AspNetCore.SignalR=Information
Serilog:MinimumLevel:Override:Microsoft=Warning
Serilog:MinimumLevel:Override:System=Warning
Serilog:MinimumLevel:Override:Windows=Warning
# Console sink — writes log output to stdout.
Serilog:WriteTo:0:Name=Console

###############################################################################
# OpenTelemetry (Metrics, Traces & Logs)
###############################################################################

# The OpenTelemetry SDK reads its configuration from OTEL_* environment
# variables. Portal therefore accepts these settings under the OpenTelemetry
# section and writes them to the matching environment variable on startup,
# which makes them available from every configuration source, including Azure
# Key Vault and Docker secret files. Setting OpenTelemetry:Endpoint also
# enables the Serilog OpenTelemetry log sink.
# The OTEL_* environment variables listed underneath each setting can still be
# used directly. They have no hierarchical (`:`) form.

# OTLP endpoint for exporting metrics, traces and logs.
# Environment variable: OTEL_EXPORTER_OTLP_ENDPOINT
OpenTelemetry:Endpoint=https://...
# Service name reported in telemetry data.
# Environment variable: OTEL_SERVICE_NAME
OpenTelemetry:ServiceName=...
# Additional resource attributes attached to all telemetry data
# (e.g. environment=production).
# Environment variable: OTEL_RESOURCE_ATTRIBUTES
OpenTelemetry:ResourceAttributes=environment=...
# Interval in milliseconds at which metrics are exported.
# Environment variable: OTEL_METRIC_EXPORT_INTERVAL
OpenTelemetry:MetricExportInterval=10000
# Protocol used for all signals. Valid values: http/protobuf, grpc.
# Environment variable: OTEL_EXPORTER_OTLP_PROTOCOL
OpenTelemetry:Protocol=http/protobuf
# Authorization header(s) for the OTLP endpoint. Typically stored as a secret.
# Environment variable: OTEL_EXPORTER_OTLP_HEADERS
OpenTelemetry:Headers=Authorization=...
# Any further OTEL_* variable can be set verbatim. The key is used as the
# environment variable name and the value as its value.
OpenTelemetry:AdditionalVariables:OTEL_TRACES_SAMPLER=always_on

###############################################################################
# Advanced / Internal — Do not change these values unless instructed.
###############################################################################

# Duration in milliseconds after which a BBV watchdog session without heartbeat
# is considered gone and removed.
StreamingConfig:WatchdogSessionTimeOut=30000
# SignalR handshake timeout in milliseconds. Set to 0 to use the internal default.
StreamingConfig:ConnectionHandshakeTimeout=0
# Maximum SignalR hub message size in bytes. Set to 0 to use the internal
# default (32 KB). Increase only if you encounter message size errors.
StreamingConfig:MaximumReceiveMessageSize=0

# ZipArchive pool settings for model hosting. Controls how many ZipArchive
# instances are kept in memory for concurrent model file access.
# Minimum number of ZipArchive instances always kept in the pool.
ModelHosting:ZipArchivePoolMinSize=2
# Maximum number of ZipArchive instances allowed in the pool.
ModelHosting:ZipArchivePoolMaxSize=10
# Interval in milliseconds to check if the pool needs additional instances.
ModelHosting:ZipArchiveCreateDuration=1000
# Interval in milliseconds to check for and remove idle instances from the pool.
ModelHosting:ZipArchiveRemoveDuration=30000
# Time in milliseconds the pool must go without being fully used before a
# surplus instance is removed. This prevents instances that were needed very
# recently from being dropped again immediately.
ModelHosting:ZipArchiveDrainCooldown=60000
# Time in milliseconds without any model file access after which the pool is
# emptied completely, ignoring ZipArchivePoolMinSize.
ModelHosting:ZipArchiveIdleTimeout=600000
# Run as standalone Kestrel web server (true) or behind IIS (false).
RunAsStandAlone=true (default)
# Automatically run database migrations on startup. When enabled, the database
# schema is created or updated to the current version.
RunMigrationsOnStartup=true (default)
```

---

# UDiTH Portal — Breaking Changes

# Breaking Changes

This page lists changes that require attention when updating an existing UDiTH
Portal installation. Changes are grouped by the release that introduced them; when
skipping releases, work through every section between the installed version and
the target version.

Database schema changes are applied automatically and are not listed here.

## 2026.4.0

### Changed default values

These defaults changed. An installation that sets the value explicitly is not
affected; an installation that relies on the default is.

| Setting                                | Old default | New default | Effect                                                                                     |
| -------------------------------------- | ----------- | ----------- | ------------------------------------------------------------------------------------------ |
| `TusSettings:TusEnabled`               | `false`     | `true`      | Resumable (chunked) uploads are enabled unless switched off                                  |
| `AccountRefreshInterval`               | *unset*     | `600000`    | Keycloak accounts are now synchronised every 10 minutes; set `0` to synchronise only at startup |
| `ModelHosting:ZipArchiveRemoveDuration`| `5000`      | `30000`     | Unused ZIP handlers are released less aggressively                                           |

### Docker: new image variants

The Portal image is now also published as `ubuntu` and `ubuntu-chiseled`, based
on Ubuntu 26.04 LTS. The existing `noble` and `noble-chiseled` variants (Ubuntu
24.04) still work but are considered legacy and will be removed with release
2027.0. See
[Setup Portal Linux](/UDiTH%20Portal/Setup%20Portal%20Linux) for the full list
and the matching volume permissions.

### Log forwarding to OpenTelemetry moved

Forwarding log output to an OTLP endpoint no longer requires a Serilog sink
entry. Setting `OpenTelemetry:Endpoint` now enables it. An existing
`Serilog:WriteTo:<n>:Name=OpenTelemetry` entry keeps working, but combined with
`OpenTelemetry:Endpoint` it causes log records to be sent twice. Remove one of
the two.

### Renamed metrics

Metric names now use dot-separated segments throughout, and counters and gauges
end in `.count`. Unit codes follow the OpenTelemetry convention. Existing
dashboards and alerts that use an old name return no data.

| Old name                                          | New name                                                 |
| ------------------------------------------------- | -------------------------------------------------------- |
| `portal.cmr.files.requested`                      | `portal.cmr.files.requested.count`                        |
| `portal.cmr.files.success`                        | `portal.cmr.files.success.count`                          |
| `portal.cmr.files.notfound`                       | `portal.cmr.files.notfound.count`                         |
| `portal.cmr.files.downloadedBytes`                | `portal.cmr.files.downloaded.bytes`                       |
| `portal.cmr.storage.zip.created`                  | `portal.cmr.storage.zip.created.count`                    |
| `portal.cmr.storage.zip.destroyed`                | `portal.cmr.storage.zip.destroyed.count`                  |
| `watchdog.webservices.events.received.start`      | `watchdog.webservices.events.received.start.count`        |
| `watchdog.webservices.events.received.stop`       | `watchdog.webservices.events.received.stop.count`         |
| `watchdog.webservices.events.received.ignored`    | `watchdog.webservices.events.received.ignored.count`      |
| `watchdog.client.events.startConnection`          | `watchdog.client.events.start.connection.count`           |
| `watchdog.viewer.start.succeeded`                 | `watchdog.viewer.start.succeeded.count`                   |
| `watchdog.viewer.start.failed`                    | `watchdog.viewer.start.failed.count`                      |
| `watchdog.viewer.signalingConnection.succeeded`   | `watchdog.viewer.signaling.connection.succeeded.count`    |
| `watchdog.viewer.launchQueue.count`               | `watchdog.viewer.launch.queue.count`                      |
| `watchdog.viewer.notResponding.count`             | `watchdog.viewer.not.responding.count`                    |
| `watchdog.viewer.start.duration.seconds`          | `watchdog.viewer.start.duration.milliseconds`             |

This release also adds a large number of new metrics, including import counters,
session and preload gauges, stream metrics and host hardware readings. See
[Observability](/UDiTH%20Portal/Observability) for the complete list.

### Render server and Portal must be updated together

The communication between Portal and the render server changed in this release.
Run the same release on Portal and on all render servers; a mixed setup is not
supported.

### Folder permissions are now split by purpose

A folder permission is no longer a single access level. It is granted separately
for streaming (browser-based viewing), local caching and opening a model locally.
Existing permissions are converted during the update so that a user who had
access keeps access to all three. Review the result in
[Permission Management](/UDiTH%20Portal/Permission%20Management) if a more
restrictive setup is wanted.

---

# UDiTH Portal — Adding Custom CA Certificates

# Adding Custom CA Certificates

When UDiTH Portal needs to communicate over HTTPS with keycloak or a database
that use a certificate issued by a **company-internal Certificate
Authority (CA)**, the CA certificate must be trusted **inside the container**.

Portal runs on .NET, which on Linux uses the operating system's OpenSSL trust
store. Adding your CA to the system trust store therefore makes it trusted by
Portal as well. There is no Portal-specific certificate setting — it is purely an
operating-system-level configuration of the base image.

The `quay.io/caxperts/portal` image is published with several base-OS variants.
The procedure differs slightly per variant:

| Tag               | Base OS                    | Runs as  | Tooling                  | Trust bundle used by .NET            |
| ----------------- | -------------------------- | -------- | ------------------------ | ------------------------------------ |
| `ubuntu`          | Ubuntu 26.04 LTS           | root     | `update-ca-certificates` | `/etc/ssl/certs/ca-certificates.crt` |
| `ubuntu-chiseled` | Ubuntu 26.04 LTS, chiseled | uid 1654 | *none (no shell)*        | `/etc/ssl/certs/ca-certificates.crt` |
| `ubi`             | RHEL 9 (UBI)               | uid 1001 | `update-ca-trust`        | `/etc/pki/tls/certs/ca-bundle.crt`   |
| `alpine`          | Alpine Linux               | root     | `update-ca-certificates` *(must be installed)* | `/etc/ssl/certs/ca-certificates.crt` |

The legacy `noble` and `noble-chiseled` variants (Ubuntu 24.04) behave exactly
like `ubuntu` and `ubuntu-chiseled`; replace the tag in the examples below if you
still use them.

> **Recommended approach:** Build a small derived image based on the Portal image
> that adds your CA certificate at build time. This works for **all** variants
> (including the chiseled one, which has no shell or package manager), produces an
> immutable, reproducible image, and requires no privileged runtime configuration.

## Prerequisites

- Your company-internal **CA certificate** in PEM format (a text file beginning
  with `-----BEGIN CERTIFICATE-----`). The file extension should be `.crt`.
  - If your CA is in DER/binary format, convert it first:
    ```bash
    openssl x509 -inform DER -in company-ca.der -out company-ca.crt
    ```
  - If there is an intermediate CA in the chain, add it as a separate `.crt`
    file as well.
- Docker available on the machine that builds the image.

Place the CA file (here called `company-ca.crt`) next to the `Dockerfile` you
create below.

## Portal images (build-time approach, recommended)

Create a `Dockerfile` for your variant, then build a derived image and use that
image instead of the original `quay.io/caxperts/portal` image in your
`docker-compose.yml`.

### `ubuntu` (Ubuntu)

```dockerfile
FROM quay.io/caxperts/portal:ubuntu
USER root
COPY company-ca.crt /usr/local/share/ca-certificates/company-ca.crt
RUN update-ca-certificates
```

### `ubi` (Red Hat UBI)

```dockerfile
FROM quay.io/caxperts/portal:ubi
USER root
COPY company-ca.crt /etc/pki/ca-trust/source/anchors/company-ca.crt
RUN update-ca-trust
# Restore the original non-root runtime user
USER 1001
```

### `alpine`

Alpine does not ship the `ca-certificates` package by default, so it is installed
as part of the build:

```dockerfile
FROM quay.io/caxperts/portal:alpine
USER root
RUN apk add --no-cache ca-certificates
COPY company-ca.crt /usr/local/share/ca-certificates/company-ca.crt
RUN update-ca-certificates
```

### `ubuntu-chiseled`

The chiseled image is a minimal (distroless-style) image with **no shell and no
package manager**, so the trust store cannot be updated inside it directly.
Instead, use a multi-stage build: regenerate the trust bundle in the full
`ubuntu` image and copy the finished bundle into the chiseled image.

```dockerfile
# Stage 1: build the updated trust bundle using the full Ubuntu image
FROM quay.io/caxperts/portal:ubuntu AS certbuilder
USER root
COPY company-ca.crt /usr/local/share/ca-certificates/company-ca.crt
RUN update-ca-certificates

# Stage 2: the actual chiseled runtime image with the updated bundle
FROM quay.io/caxperts/portal:ubuntu-chiseled
COPY --from=certbuilder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
```

### Building the image

From the folder containing the `Dockerfile` and `company-ca.crt`:

```bash
docker build -t portal-custom-ca:ubuntu .
```

Then reference the new image tag in your `docker-compose.yml`:

```yml
services:
  portal_demo:
    image: portal-custom-ca:ubuntu
    # ... remaining configuration unchanged ...
```

If you maintain an internal container registry, tag and push the derived image
there so all your hosts can pull it:

```bash
docker tag portal-custom-ca:ubuntu registry.example.com/udith/portal-custom-ca:ubuntu
docker push registry.example.com/udith/portal-custom-ca:ubuntu
```

## Windows (non-container) installations

The container variants above apply to the Linux/Docker deployment. The **Windows**
version of UDiTH Portal does not use these bundles — .NET on Windows uses the
**Windows certificate store**. To trust a company-internal CA there, import the CA
certificate into the **Trusted Root Certification Authorities** store of the
**Local Machine** (not the current user), so the Portal service account trusts it.

Using PowerShell (run as Administrator):

```powershell
Import-Certificate -FilePath C:\path\to\company-ca.crt `
  -CertStoreLocation Cert:\LocalMachine\Root
```

Alternatively, using `certutil`:

```cmd
certutil -addstore -f Root C:\path\to\company-ca.crt
```

Or via the GUI: run `certlm.msc` → **Trusted Root Certification Authorities** →
**Certificates** → right-click → **All Tasks** → **Import**, then select the CA
file. Import an intermediate CA into the **Intermediate Certification
Authorities** store. Restart the Portal service afterwards so it picks up the new
trust.

## Keycloak server (`quay.io/keycloak/keycloak`)

If you run your own Keycloak and it must trust a company-internal CA (for example
to call an internal LDAP/AD over LDAPS, an internal identity broker, or an SMTP
server over TLS), the certificate has to be trusted by **Keycloak**, not by
Portal.

Keycloak is a **Java** application, so it does **not** use the OpenSSL system
bundle that the Portal images use. It uses the **Keycloak System Truststore**.
The Keycloak 26.7.2 image is based on a *minimized* RHEL 9 UBI and runs as the
non-root user `keycloak` (uid 1000).

> The existing Java default truststore certificates are always trusted. You only
> need the steps below for self-signed certificates or internal Certificate
> Authorities that the JRE does not recognise.

### Keycloak System Truststore (recommended, no rebuild)

Keycloak automatically loads any certificate placed in the
`conf/truststores` directory (or its subdirectories, scanned recursively) into
its **System Truststore** at startup. This becomes both the default
`javax.net.ssl` truststore and the default for Keycloak's internal usage.

Supported file formats in `conf/truststores`:

- **PEM** files (a text file beginning with `-----BEGIN CERTIFICATE-----`).
- **PKCS12** files with extension `.p12`, `.pfx`, or `.pkcs12`. These must be
  **unencrypted** — no password is expected.

Mount your CA certificate into that folder:

```yml
services:
  keycloak:
    image: quay.io/keycloak/keycloak:x.y.z
    command: start
    volumes:
      - ./company-ca.crt:/opt/keycloak/conf/truststores/company-ca.crt:ro
    # ... remaining configuration unchanged ...
```

You can place multiple files in the folder (for example a root and an
intermediate CA). On startup Keycloak logs that it has loaded them:

```
INFO  [org.keycloak.truststore.TruststoreBuilder] Found the following truststore files
in the truststore paths [/opt/keycloak/conf/truststores/company-ca.crt]
```

### Alternative — import into the Java `cacerts` at build time

Alternatively, bake the CA into the JVM default truststore (`cacerts`) using
`keytool`. Use this only if you specifically need the CA trusted by the JVM
default store rather than via the Keycloak System Truststore above.

```dockerfile
FROM quay.io/keycloak/keycloak:x.y.z
USER root
COPY company-ca.crt /tmp/company-ca.crt
RUN keytool -importcert -noprompt -trustcacerts -alias company-internal-ca \
    -file /tmp/company-ca.crt -cacerts -storepass changeit \
    && rm /tmp/company-ca.crt
USER 1000
```

Build it and reference the derived image instead of the original:

```bash
docker build -t keycloak-custom-ca:26.7.2 .
```

> `changeit` is the well-known default password of the Java `cacerts` store; it is
> not a secret. The `-cacerts` flag targets the JVM's default truststore
> automatically, so you do not need to know its path.

## Verifying the certificate is trusted

After building (or starting the container), confirm the CA is present in the
trust bundle.

For variants with a shell (`ubuntu`, `ubi`, `alpine`):

```bash
# ubuntu / alpine
docker run --rm --entrypoint sh portal-custom-ca:ubuntu -c \
  "awk -v RS='-----END CERTIFICATE-----' '/Company Internal CA/{print \"found\"}' \
   /etc/ssl/certs/ca-certificates.crt; \
   openssl x509 -in /usr/local/share/ca-certificates/company-ca.crt -noout -subject"

# ubi
docker run --rm --entrypoint sh portal-custom-ca:ubi -c \
  "grep -c 'Company Internal CA' /etc/pki/tls/certs/ca-bundle.crt"
```

For `ubuntu-chiseled` (no shell), copy the bundle out and inspect it on the host:

```bash
id=$(docker create portal-custom-ca:ubuntu-chiseled)
docker cp "$id:/etc/ssl/certs/ca-certificates.crt" ./bundle.crt
docker rm "$id"
grep -c "BEGIN CERTIFICATE" ./bundle.crt   # bundle now includes your CA
```

The most reliable functional test is to let Portal connect to your internal
HTTPS endpoint (for example Keycloak). If the CA is trusted, the previous TLS
errors such as `The remote certificate is invalid according to the validation
procedure` / `unable to get local issuer certificate` disappear from the Portal
logs.

For **Keycloak**, when using the System Truststore (`conf/truststores`) check the
startup log — Keycloak lists each loaded certificate at debug level:

```bash
docker run --rm \
  -v "$PWD/company-ca.crt:/opt/keycloak/conf/truststores/company-ca.crt:ro" \
  quay.io/keycloak/keycloak:26.7.2 \
  start-dev --log-level=org.keycloak.truststore:debug 2>&1 | grep -i "truststore"
# Expect: "Trusted root CA found in truststore ... Subject DN : CN=<your CA>"
```

When using the `cacerts` build-time method instead, confirm the import:

```bash
docker run --rm --entrypoint sh keycloak-custom-ca:26.7.2 -c \
  "keytool -list -cacerts -storepass changeit | grep -i company-internal-ca"
```

## Notes

- Replace the example subject `Company Internal CA` / alias `company-internal-ca`
  in the verification commands with your actual CA's common name or alias.
- Re-create the derived image whenever you pull a newer Portal or Keycloak base
  image, so the CA stays present and the public CA list stays current.
- Only add CA certificates you control and trust. Adding a CA means the container
  will trust **any** certificate that CA issues.

---

# UDiTH Portal — Setup Renderserver

# Setup Renderserver

## Requirements

See the [Requirements](05%20Requirements.md#render-server) page for the render server hardware and operating system requirements.

## Sample Cloud and On-Premises Setups

The following examples show some possible hardware configurations for the render server in cloud and on-premises environments.
Multiple render servers can be used on one Portal for load balancing.

### Oracle

For approximately 8-10 simultaneous streams:

Oracle `VM.GPU3.1` with `16 GB` GPU VRAM, or `2 × VM.GPU2.1 / 2.x`.
For higher stream counts, scaling up to `VM.GPU3.x` is recommended.

### Azure

For approximately 8-10 simultaneous streams:

Minimum: `Standard_NV24s_v3` (`16 GB` VRAM).

Recommended for more streams:

`Standard_NV48s_v3` (`M60`, `32 GB` VRAM) or `Standard_NV36ads_A10_v5` (`24 GB` VRAM).

### AWS

For approximately 8-10 simultaneous streams:

AWS EC2 `G3` with `2` GPUs (`16 GB` VRAM).
For more streams, an AWS EC2 `G5` instance is recommended with multiple GPUs.

### On-Premises

NVIDIA GPU, for example `A4500 ADA`, with `2 GB` VRAM and `4 GB` RAM per stream.
Minimum `8` CPU cores. The final setup may vary depending on the use case.

## Setup Renderservice

Click **Next**.

![Setup](media/RenderServerInstallation_01_setup.png)

To continue with the installation, accept the EULA and click **Next**.

![EULA](media/RenderServerInstallation_02_EULA.png)

Enter an installation location or use the default location.

![Location](media/RenderServerInstallation_03_Location.png)

Enter a name for the Watchdog so that it can be identified later.
Then enter the UDiTH Portal URL, `https://<alias/(sub)domain>/Portal`, together with the **Signaling API Credential**. This is the client secret of the `signaling-api` client in Keycloak, which is automatically created during Portal installation. You can find it in the Keycloak admin console under **Clients → signaling-api → Credentials**.
If batch mode is enabled, no UDiTH windows are shown during execution. If **Ignore certificate errors** is enabled, invalid certificates from the Portal are also accepted.

![Setting](media/RenderServerInstallation_04_Setting.png)

Start the installation by clicking **Install**.

![StartInstall](media/RenderServerInstallation_05_StartInstall.png)

Click **Finish** to complete the installation. If the checkbox is selected, the render server starts automatically.

![Finish](media/RenderServerInstallation_06_Finish.png)

## Environment Variables

The render server's behaviour can be adjusted via environment variables.

| Variable | Description |
|----------|-------------|
| `UDITH_APPCONTROL_DISABLEGPU` | Disables GPU (hardware) acceleration for AppControls. GPU acceleration is enabled by default but can cause issues on some render servers. Set the variable to `true` (case-insensitive) to disable it. Note that disabling GPU acceleration breaks WebGL apps and can make very heavy apps slower, although small apps may run faster. |

## Security

The user account used to run Renderserver should be a low-privilege user.

---

# UDiTH Portal — Setup ICE Server

# Setup ICE Server

The ICE server helps the client connect to the render server.
This is done by setting up a TURN/STUN server for address and port resolution, as well as proxying for the WebRTC protocol.

## Requirements

- Ensure that ports `3478 TCP/UDP` are open to clients and render servers.
  - It is possible to use different ports.
- Ensure that ports `50000-51000 UDP` are open to clients and render servers.
  - It is possible to use different port ranges.
- A **Windows** host is required for the standalone CAXturn installation.
- On **Linux**, CAXturn is available as a Docker image (`quay.io/caxperts/caxturn`). See the Linux setup guide for a sample `docker-compose.yml`.

## Setup

During installation, CAXturn is provided together with a sample configuration file.

### Configuration

```json
{
    "publicIp": "1.1.1.1",
    "username": "1234",
    "password": "5678",
    "realm": "caxnet",
    "minPort": 50000,
    "maxPort": 51000,

    "enableTCP": true,
    "tcpPort": 3478,

    "enableUDP": true,
    "udpPort": 3478
}
```
| Key         | Description                                                                 |
|-------------|-----------------------------------------------------------------------------|
| `publicIp`  | The public IP address of the server or service. |
| `username`  | The user name used for authentication. |
| `password`  | The password used for authentication. |
| `realm`     | The authentication realm, typically representing a domain or service name. The value itself is not important, but it must be specified. |
| `minPort`   | The minimum port number for dynamic UDP port allocation. |
| `maxPort`   | The maximum port number for dynamic UDP port allocation. |
| `enableTCP` | Enables the TCP protocol for initial communication when set to `true`. |
| `tcpPort`   | The port number used for initial TCP communication. |
| `enableUDP` | Enables the UDP protocol for initial communication when set to `true`. |
| `udpPort`   | The port number used for initial UDP communication. |

### Running

By default, running `CAXturn.exe` is sufficient, as it already searches for a `config.json` file. A manual file can be specified by using `-config`.

It is recommended to set up a scheduled task to run CAXturn after a reboot.

### Turn URLs

To use the ICE server in Portal, the URLs must be specified in the following format:

`turn:<hostname/ip/subdomain>:<udp-port>`

`turn:<hostname/ip/subdomain>:<tcp-port>?transport=tcp`

## Alternatives

There are alternatives to CAXturn. It is possible to use cloud TURN hosting providers or tools such as `coturn`.

---

# UDiTH Portal — Central Model Repository

# Central Model Repository

The Central Model Repository (CMR) was introduced with Portal and replaces both the model hosting folder and the model folder on the render server with a centrally managed system. UDiTH models are imported into the CMR so that they are available across your organisation.

## Concept

### Model

A model is created when a model version is imported for the first time. It is identified by a model identifier. Each model can have multiple model versions. The model identifier must be unique. Each model also stores a description, tags, title, preview image, and logical path.

The identifier can contain alphanumeric characters and `_`.

### Modelversion

Each model version is identified by a combination of the model identifier and the model version identifier. The latest model version is also available through the alias tag `latest`. It is not possible to assign the model version identifier 'latest' manually. When a model is requested, a specific model version is always required. If no version is specified, the alias tag `latest` is used.

The same model version identifier can be used across multiple models, but it must be unique within a single model. Each model version is only a snapshot in time and contains no delta information.

The identifier can contain alphanumeric characters and `_`.

### Revision

A revision changes individual files of an existing model version without a new import. Revisions are numbered per model version (`1`, `2`, ...) and are applied on top of each other. The model version without any revisions is available as revision `0`. For details, see [Revisions](#revisions).

### Storage

Storage is managed entirely by Portal. It is not possible to intervene manually in the process.

### Import

A model must be imported in order to transfer it into the storage system. During import, the model identifier and model version identifier are specified and other settings must also be defined.

### Attribute Import

It is possible to extract attributes into a tag register, which allows Portal search to find model objects, both 3D and PID, across multiple models.

## Import Process

> ⚠️WARNING⚠️ In the current version, models must be zipped by the administrator before import. This is expected to change in future UDiTH Builder versions, where this process will be automated. Ensure that the `Data` folder is at the top level inside the ZIP archive. Otherwise, the model will be rejected during the import process.


1. Place the ZIP file in the import folder on the server.
1. Navigate to the **CMR -> Model import** section.
1. Select the model to import.
![Model Import Selection](media/Portal_model_import_selection.png)
1. Choose either a new model or an existing model to which a new model version should be added.
![Model Import new or existing model](media/Portal_model_import_select.png)
1. Fill in the form by specifying the name, model ID, folder, version ID, preview image, description, tags, and the number of preloaded and maximum instances for BBV. For the tag register, you can choose which attributes to import. Import can also be disabled either here or globally through configuration. ⚠️ **Do not import all attributes.**
![New Model](media/Portal_model_new_model.png)
1. After the form has been submitted, the model import process starts. The model becomes available for viewing once the transfer to storage has been completed. Attribute import may take longer.
1. If you choose an existing model, the form is prefilled with the contents of the previous import.

## Folders

Models are organised in folders, and permissions are assigned per folder (see [Permission Management](/UDiTH%20Portal/Permission%20Management)). A folder that does not exist yet is created automatically during import, for example from `ParentFolderPath` in `modelDefinition.json`. Such a new folder has no permissions of its own, so the model is only visible to users who have permissions on a parent folder.

To set up permissions before the first import, create the folders ahead of time:

- **Administration → Access** (Admin only): select a parent folder in the folder tree and click **Add folder**.
- **Import form** (Admin or Browser Maintainer): open the folder selection, select a parent folder and click **Add folder**. Browser Maintainers can only create folders below a folder on which they hold a permission.

Rules:

- Folders can only be created inside the models folder.
- Folders cannot be created inside a model.
- The folder name must not contain `/` and can have a maximum of 200 characters.

## Automated Import

It is possible to automate the process by specifying a `modelDefinition.json` file inside the `Data` folder.

Many attributes can be ignored and may be left empty or removed. In that case, the default behaviour is used, or the previous value if a new model version is being added to an existing model.

During the first import of a model, ensure that the `Model` section is filled in completely. If the model already exists, only `ExternalIdentifier` is required. If other values are specified, they also overwrite the corresponding attributes of the existing model.

If `AutoApprove` is set, an automatic import is attempted. If this fails for any reason, a manual approval can still be attempted.

### Format
```json
{
    "Cmr": {
        "ProcessOptions": {
            "SkipProcessing": false,
            "MaxRetainedModelVersions": null,
            "AutoApprove": false,
            "KeepAllPreloads": null
        },
        "ModelVersion": {
            "ExternalIdentifier": "REQUIRED",
            "Description": null,
            "Tags": null,
            "AttributesPidProcess": null,
            "Attributes3DProcess": null,
            "IsProtected": false,
            "DesiredPreloadInstances": null,
            "MaximumInstances": null
        },
        "Model": {
            "ParentFolderPath": null,
            "Name": "REQUIRED on first import else can be null",
            "ExternalIdentifier": "REQUIRED",
            "ModelAliases": null
        }
    }
}
```

#### ProcessOptions

| Field | Type | Description |
|-------|------|-------------|
| `SkipProcessing` | `bool?` | Skip attribute extraction for this import. When `null`, the global `ModelProcessing__ForcedSkipProcessing` setting is used. When `true`, the model is imported but no attributes are written to the database. |
| `MaxRetainedModelVersions` | `int?` | Maximum number of model versions to keep for this model. When a new version is imported and the limit is exceeded, the oldest unprotected versions are automatically marked for deletion. Leave `null` to keep all versions. |
| `AutoApprove` | `bool?` | When `true`, the import proceeds automatically without manual approval in the web interface. If automatic approval fails for any reason, manual approval can still be attempted. |
| `KeepAllPreloads` | `bool?` | When `true`, all existing model versions retain their `DesiredPreloadInstances` value after a new version is imported. When `false` or `null`, preload instances of all previous versions are set to `0`, so that only the latest version is preloaded for BBV. |

#### ModelVersion

| Field | Type | Description |
|-------|------|-------------|
| `ExternalIdentifier` | `string` | **Required.** Unique identifier for this model version within its model. Can contain alphanumeric characters and `_`. |
| `Description` | `string?` | Optional description of the model version. |
| `Tags` | `string[]?` | Optional list of tags for categorisation and search. |
| `AttributesPidProcess` | `string[]?` | List of PID attribute names to extract into the tag register. ⚠️ Do not import all attributes. |
| `Attributes3DProcess` | `string[]?` | List of 3D attribute names to extract into the tag register. ⚠️ Do not import all attributes. |
| `IsProtected` | `bool` | When `true`, this model version is protected from automatic deletion by retention rules (`MaxRetainedModelVersions`). Protected versions are excluded from retention calculations. Default: `false`. |
| `DesiredPreloadInstances` | `int?` | Number of BBV instances to preload for this version. Preloaded instances reduce initial load time for users. When not set, the value from the previous version is used. |
| `MaximumInstances` | `int?` | Maximum number of concurrent BBV instances allowed for this version. When not set, the value from the previous version is used. |

#### Model

| Field | Type | Description |
|-------|------|-------------|
| `ExternalIdentifier` | `string` | **Required.** Unique identifier for the model. Can contain alphanumeric characters and `_`. Used to match imports to existing models. |
| `Name` | `string` | **Required on first import.** Display name of the model. On subsequent imports, this can be `null` to keep the existing name, or set to overwrite it. |
| `ParentFolderPath` | `string?` | Logical folder path for organising models in the web interface (e.g. `"Demoplants"`). |
| `ModelAliases` | `string[]?` | Alternative identifiers for the model. These aliases can be used to reference the model in addition to the primary `ExternalIdentifier`. |

## ModelUploader CLI

`ModelUploader.exe` is a command-line tool that uploads a model ZIP file to Portal. After the upload, the model appears in the import as described in [Import Process](#import-process). To import without manual approval, add a `modelDefinition.json` with `AutoApprove` to the ZIP file (see [Automated Import](#automated-import)).

### Authentication

The tool signs in with a Keycloak service account. The service account needs the **Admin** role (`CaxpertsAdmin`).

1. Create a Keycloak client with **Service accounts roles** enabled. See [Advanced integration options](/UDiTH%20Portal/Advanced%20integration%20options), section *How to create a service account*.
1. Assign the realm role `CaxpertsAdmin` to the service account of the client.
1. Copy the client secret from the **Credentials** tab of the client.

### Configuration

Enter the settings in `appsettings.json` next to `ModelUploader.exe`:

```json
{
  "TusSettings": {
    "TusEndpoint": "https://<portal-url>/api/files"
  },
  "Keycloak": {
    "ClientId": "<client-id>",
    "ClientSecret": "<client-secret>",
    "TokenEndpoint": "https://<keycloak-url>/realms/<realm>/protocol/openid-connect/token"
  }
}
```

You can also set the values as environment variables, for example `Keycloak__ClientSecret`. Environment variables override `appsettings.json`.

### Usage

```cmd
ModelUploader.exe upload C:\Models\my-model.zip
```

| Option | Description |
|--------|-------------|
| `--no-wait` | Exit as soon as Portal has received the file, without waiting for Portal to process it. |
| `--log <path>` | Write the log file to the given path. |

### Exit Codes

| Code | Meaning |
|------|---------|
| `0` | Success. The model is ready for import. |
| `10`, `11` | The file does not exist or cannot be opened. |
| `20` | The configuration is missing or invalid. The output lists all problems. |
| `30`–`33` | Sign-in with Keycloak failed. Check the client ID, the client secret and the token endpoint. |
| `40`–`44` | The upload failed. Check the connection to Portal and the Admin role of the service account. |
| `50` | Portal rejected the file, for example because it is not a valid model ZIP. |
| `51`, `52` | Portal received the file, but the result is not known yet. Check the import in Portal before you upload again. |
| `60` | The upload was cancelled. |

## Model Aliases

Model aliases are alternative identifiers for a model. They can be set during import via the `ModelAliases` field in `modelDefinition.json` or edited in the model settings. When a model is requested by identifier, the system first checks for an exact match on the `ExternalIdentifier`. If no match is found, it searches the aliases.

This is useful when a model needs to be referenced by different names, for example, when the external identifier follows an internal naming convention but users should be able to access it by a friendlier name.

Aliases can also be used to create shareable model links. For details, see the [Linking](/UDiTH%20Portal/Linking) page.

### Example First Import
```json
{
    "Cmr": {
        "ProcessOptions": {
            "AutoApprove": true
        },
        "ModelVersion": {
            "ExternalIdentifier": "2025_10_23",
            "Description": "this is optional",
            "Tags": ["this", "is", "also", "optional"],
            "AttributesPidProcess": ["PID", "attributes", "to", "import"],
            "Attributes3DProcess": ["3D", "attributes", "to", "import"],
            "DesiredPreloadInstances": 1,
            "MaximumInstances": 5
        },
        "Model": {
            "ParentFolderPath": "Demoplants",
            "Name": "Sample Demoplant",
            "ExternalIdentifier": "sample"
        }
    }
}
```

### Example Second Import
```json
{
    "Cmr": {
        "ProcessOptions": {
            "AutoApprove": true
        },
        "ModelVersion": {
            "ExternalIdentifier": "2025_10_24"
        },
        "Model": {
            "ExternalIdentifier": "sample"
        }
    }
}
```

## Revisions

A revision changes individual files of an existing model version, for example colour files, drawing templates, the standard catalogue or `projectInfo.bytes`. A new import is not necessary. The base model version stays unchanged in storage. Portal applies the revisions when the model is opened.

### Behaviour

- A revision starts as a **Draft**. Only Admins and Browser Maintainers can see and edit drafts.
- When you publish a revision, it gets the next number (`1`, `2`, ...). A published revision can no longer be changed.
- Published revisions are cumulative. Revision `2` contains the changes of revision `1` and its own changes.
- When a model version is opened without a revision, all published revisions are applied.
- The model version without any revisions is available as revision `0` (**Base version**). It can be opened, but not edited.
- A draft starts from the current state, that is the base version with all published revisions.
- When a model version is deleted, its revisions are also deleted.

### Permissions

Admins and Browser Maintainers can create, edit, publish and delete revisions. Browser Maintainers need a permission on the folder of the model. Other users with access to the model see only the published revisions and the base version, and can open them.

### Protected Files

Some files of the model are generated by UDiTH Builder and cannot be changed in a revision:

- **Hidden** (not shown, no download): geometry and attribute data, for example `Data/lod*nodes`, `Data/attributeSets`, `Data/octree.bytes`, license files, drawings, documents, IntelliPIDs, textures, pictures and the PDF cache.
- **Read-only** (download only): `Data/modelDefinition.json`.

All other files and folders can be changed.

### Create and Publish a Revision

1. In the CMR overview, open the model version.
1. In the **Revisions** section, click **Create new revision**.
1. Enter a description and click **Save**.
1. In **Revision Files**, change the files:
   - Upload a file to add it or to replace an existing file.
   - Create a folder.
   - Delete a file or folder.
   - Download a file.
1. The list **Files changed** shows all changes of the draft. To undo a change, delete the entry from this list.
1. Click **Publish**. The revision becomes available to all users with access to the model.

To delete a draft, open it and click **Delete**.

### Open a Revision

Each published revision and the base version has open buttons on its card in the **Revisions** section. Model links can also open a specific revision (see [Linking](/UDiTH%20Portal/Linking)):

```
https://<portal-url>/modellink/<alias-or-identifier>/<version>/revision_<number>
```

| Example | Result |
|---------|--------|
| `/modellink/myplant/latest` | Latest model version with all published revisions |
| `/modellink/myplant/2025_10_23/revision_1` | Version `2025_10_23` with revision `1` |
| `/modellink/myplant/2025_10_23/revision_0` | Version `2025_10_23` without revisions (base version) |

The number alone (`/modellink/myplant/2025_10_23/1`) is also accepted. Drafts cannot be opened with a link.

---

# UDiTH Portal — Linking

# Linking

Model links are the recommended way to create shareable links and deep links to a model in UDiTH. UDiTH Portal generates and manages these URLs for you: it resolves the model alias, selects the requested version, applies the Portal authentication, and checks the permissions of the user before the model is opened.

A model link is a normal HTTPS URL. You can use it in documents, websites, e-mails, and reports, and it stays valid when a new model version is published.

> ℹ️ **When to use this page**
>
> - **With UDiTH Portal:** use a model link, as described below. This is the recommended method.
> - **Without UDiTH Portal:** build the deep link manually with the `upvapi://` URL handler. See [Linking and API](/UDiTH%20App/Linking%20and%20API).

## URL format

```
https://<portal-url>/modellink/<alias-or-identifier>
https://<portal-url>/modellink/<alias-or-identifier>/<version>
```

The `<alias-or-identifier>` is the external identifier of a model in the [Central Model Repository](/UDiTH%20Portal/Central%20Model%20Repository), or one of its aliases.

If no version is specified, the `latest` version is used.

## Query Parameters

| Parameter | Default | Description |
|-----------|---------|-------------|
| `openLocal` | `true` | When `true`, returns a `upvapi://` link that opens the model in the local UDiTH viewer. When `false`, returns an `https://` link that opens the model in the browser (BBV). |
| `commands` | — | Optional commands to pass to the viewer. Multiple commands are separated by `&`. See below. |

## Commands

The `commands` parameter accepts all UDiTH deeplink commands. Commands are concatenated with `&` and must be URL-encoded in the query string.

A query condition uses the syntax `<key>=<value>` and filters or selects objects by the given attribute key and value. A command uses the syntax `CMD!<Command>`, with an optional value after `=`.

**Example (decoded):**
```
Name=D-240&CMD!Select&CMD!Fit&CMD!Highlight
```
This filters by attribute `Name` with value `D-240`, then applies selection, fits the camera to it, and highlights it.

**Example (encoded):**
```
commands=Name%3DD-240%26CMD%21Select%26CMD%21Fit%26CMD%21Highlight
```
This is the same example after URL encoding, as it must be written in the query string. `=` becomes `%3D`, `&` becomes `%26`, and `!` becomes `%21`.

### Wildcards

Use `_` for any single character and `*` for any number of characters.

### Condition Operators

By default, query conditions are combined using **AND**:

```
Name=K-001&NozzleName=N5
```

However, you can use **OR** with the combine command:

```
Name=K-001&Name=D-100&CMD!Combine=OR
```

### Selection, visibility and appearance

| Command | Description |
|---------|-------------|
| `Select` | Selects the specified object.<br>**Example:** `Name=D-100&CMD!Select` |
| `ShowOnly` | Sets all query objects to visible and hides all other objects. |
| `Highlight` | Highlights the specified object.<br>**Example:** `Name=D-100&CMD!Highlight` |
| `ClearHighlight` | Disables highlighting. |
| `ClearSelection` | Clears the current selection. |
| `Combine` | `Combine=[OR/AND]` uses `OR` between query conditions instead of `AND`, which is the default. |
| `SearchFallback` | Automatically adds the search string to the search box. If the exact search returns no results, a fuzzy search is started automatically. |
| `SetVisibility` | `SetVisibility=[True/False]` |
| `SetVisibleAspects` | `SetVisibleAspects=[aspect!aspect]`<br>**Example:** `CMD!SetVisibleAspects=Simple Physical!Insulation` shows both insulation and simple physical. |
| `Color` | `Color=[HTML colour code]` using the format `#RRGGBB` or `#RRGGBBAA`.<br>**Example:** `CMD!Color=#FF0000` |
| `ClearColor` | Restores the original object colours. |
| `ClearCustomAttributes` | Resets all changes made to custom attributes during the current session. |
| `SetTreeConfiguration` | `SetTreeConfiguration=[discipline!discipline...]`<br>**Example:** `CMD!SetTreeConfiguration=Task!SystemPath` |

### Camera, clipping and screenshots

| Command | Description |
|---------|-------------|
| `Fit` | Fits the camera to the current selection. Optional parameter: `Centred`.<br>`true` (default): fits to the centre of the box.<br>`false`: fits to the screen borders of the current view.<br>**Example:** `CMD!Fit=true` |
| `ResetView` | Resets the camera mode, camera position, rotation, and states such as highlighting to the state used when the model was loaded. |
| `SetCameraView` | `SetCameraView=[X!Y!Z!rotationX!rotationY!rotationZ]` using Euler angles.<br>**Example:** `CMD!SetCameraView=3037!2476!72!180!180!90` sets the camera to coordinates `3037` (X), `2476` (Y), and `72` (Z = height), facing parallel to the ground and rotated `90°` to the left. |
| `SetCameraViewLookAtTarget` | `SetCameraViewLookAtTarget=[X!Y!Z!TargetX!TargetY!TargetZ]`<br>Moves the camera to the specified location and points it towards the target location. |
| `SetClippingPlane` | Sets the visible camera view distance. The first parameter is the minimum distance for visible objects, and the second is the maximum distance.<br>**Example:** `CMD!SetClippingPlane=20!30` |
| `IntelliClip` | `IntelliClip=[distance in metres]`<br>Values `> 0` display only the object. `0` also displays objects touching the selected object. Decimal values are supported. |
| `VolumeClip` | `VolumeClip=[distance in metres]`<br>Values `< 0` are treated as `0`. Decimal values are supported. |
| `VolumeClipByCoordinates` | Runs the volume clip algorithm using a bounding box defined by the following parameters, in this order: X min, Y min, Z min, X max, Y max, Z max.<br>**Example:** `CMD!VolumeClipByCoordinates=-10.0!-10.0!-10.0!20.0!20.0!20.0` |
| `ClearClipping` | Disables clipping. |
| `TakeAndSaveScreenshot` | Takes a screenshot using the following parameters, in this order: path where the image is to be stored, width in pixels, height in pixels, field of view in degrees (`> 160` may produce incorrect images), format (either `.png` or `.jpg`), hide UI (`true` or `false`), enable anti-aliasing (`true` or `false`), enable transparent background (`true` or `false`), model in best quality (`true` or `false`).<br>**Example:** `CMD!TakeAndSaveScreenshot=C:\Test\image.jpg!2000!1500!120!png!true!true!true!false` |

### Files, messages and application

| Command | Description |
|---------|-------------|
| `LoadColorFile` | `LoadColorFile=[online or offline location]`<br>Local: `CMD!LoadColorFile=C:\colors.xlsx`<br>Via HTTP: `CMD!LoadColorFile=http://example.com/colors.xlsx`<br>Via UNC: `CMD!LoadColorFile=file://////server/share/colors.xlsx`<br>Note: for UNC paths, it is important to use six slashes (`//////`) after `file`. |
| `LoadConfigFile` | Loads a configuration file. Parameters: configuration file name, and combine mode (optional).<br>By default, existing settings are cleared. `Merge` keeps existing views and merges them, overriding existing values with those from the configuration file.<br>**Example:** `CMD!LoadConfigFile=C:/config.upv!Merge` |
| `LoadLinkFile` | `LoadLinkFile=[online or offline location]`<br>Local: `CMD!LoadLinkFile=C:\links.xlsx`<br>Via HTTP: `CMD!LoadLinkFile=http://example.com/links.xlsx`<br>Via UNC: `CMD!LoadLinkFile=file://////server/share/links.xlsx`<br>Note: for UNC paths, it is important to use six slashes (`//////`) after `file`. |
| `ClearLinks` | Removes links loaded from link files. |
| `LoadPackageFile` | Loads package files from the specified path. Parameters: URL, and `Replace` (optional). If `Replace` is set to `true`, current packages are replaced instead of being added to the existing ones.<br>**Example:** `CMD!LoadPackageFile=c:\package.upvf!true` |
| `ExportPackageFile` | Exports package files to the specified path. |
| `LoadSketch` | Loads a sketch from the specified path. A second parameter can be used to rename the sketch.<br>**Example:** `CMD!LoadSketch=C:\Sketch\SketchFile.upvsk!NewName` |
| `ShowMessage` | Shows a message and title in a message box.<br>**Example:** `CMD!ShowMessage=Your Message!Your Title` |
| `FocusViewer` | Brings the UDiTH window to the foreground and gives it the input focus. This command only has an effect on a local model. |
| `SetStorageVariable` | Sets a storage variable. Parameters, in this order: the key to set, the value, and an optional persist flag (`true` or `false`, default `false`). Use this command with the LocalStorage Universal API system to send a message to a running AppControl.<br>**Example:** `CMD!SetStorageVariable=MyKey!MyValue` |

## Examples

| URL | Behaviour |
|-----|-----------|
| `/modellink/myplant` | Opens the latest version of model `myplant` in local viewer |
| `/modellink/myplant/2025_10_23` | Opens version `2025_10_23` in local viewer |
| `/modellink/myplant?openLocal=false` | Opens latest version in browser (BBV) |
| `/modellink/demoplant?openLocal=false&commands=Name%3DD-240%26CMD%21Select%26CMD%21Fit%26CMD%21Highlight` | Opens model `demoplant` in browser, selects object `D-240`, fits and highlights it |

> ℹ️ The user must be authenticated to use model links. Unauthenticated users are redirected to the login page first.

---

# UDiTH Portal — Permission Management

# Permission Management

UDiTH Portal uses a role-based and folder-based permission system. Users are managed through Keycloak and synchronised into Portal. Access to models and content is controlled by assigning groups to folders, separately for each way a model can be opened.

## Roles

Roles are defined in Keycloak and synchronised to Portal. Each user can have one or more of the following roles:

| Role | Description |
|------|-------------|
| **Admin** | Full access to all features, folders, and models. Can manage users, groups, permissions, and system settings. Admins bypass all folder-level permission checks. |
| **Browser Maintainer** | Can create, edit, and delete models and model versions. Required for model management operations. |
| **Group Maintainer** | Can manage group memberships and assignments. |

Users without any of these roles are regular users. They can view and interact with models based on the folder permissions assigned to their groups.

## Groups (Organisations)

Groups are the primary unit for assigning permissions. Users are assigned to one or more groups, and permissions are granted to groups on a per-folder basis.

Groups can be managed in two ways:

- **Manually** through the Portal administration interface at **Administration → Groups**.
- **Automatically** through federated identity providers (e.g. Azure AD). See the [federated accounts guide](/UDiTH%20Portal/Using%20federated%20accounts) for details on group synchronisation.

Groups can be organised in a tree structure, allowing hierarchical organisation management.

> ⚠️ **Important:** Group assignments are inherited **upwards**. If a user is assigned to a child group, they are also considered a member of all parent groups in the hierarchy. Keep this in mind when structuring your group tree and assigning folder permissions.

## Folder-Based Permissions

Access to models is controlled through **folders**. Each model is placed in a folder, and permissions are assigned to groups at the folder level.

### Permission Types

A permission is not a single access level. It is granted separately for each way a model can be opened, so a group can be allowed to view a model in the browser without being allowed to download it, and vice versa.

| Name | Grants |
|------|--------|
| **Streaming** | Opening the model in the locally installed UDiTH application. The model data is streamed from Portal and is not stored on the client. |
| **Cache** | Caching the model locally in UDiTH, so it can be opened again without a connection to Portal. |
| **BrowserBasedViewing** | Opening the model in the browser. Requires a render server and a browser-based-viewing licence. |
| **Visibility** | Not granted directly. A folder and the models in it become visible as soon as a group holds at least one of the permissions above. |

The permission types are independent. Granting **Cache** without **Streaming** is a valid configuration, and each type can be granted to a different set of groups.


### How Permissions Are Resolved

1. **Admins** always have full access to all folders and models.
2. For other users, Portal determines which groups the user belongs to and, for the permission type the requested action needs, whether any of those groups holds that permission on the folder.
3. Each permission type is resolved on its own. A user who only holds **BrowserBasedViewing** on a folder sees its models and can open them in the browser, but cannot open or cache them locally.
4. Permissions are inherited along the folder hierarchy. A permission set on a parent folder applies to all child folders unless overridden.

### Assigning Permissions

Permissions are managed in the Portal administration interface:

1. Navigate to **Administration → Access**.
2. Select the folder for which you want to manage permissions in the folder tree.
3. Add a permission for a group and select which of the permission types it should hold.

The folder tree shows the permission types currently assigned per folder and group, so an existing configuration can be reviewed without opening each entry.

> **Upgrading from a release before 2026.4.0:** permissions used to be a single access level. During the update, every group that had access to a folder receives **Streaming**, **Cache** and **BrowserBasedViewing** on that folder, so nobody loses access. Review the result here if a more restrictive setup is wanted. See [Breaking Changes](/UDiTH%20Portal/Breaking%20Changes).

## Model Access

Model access is derived from the folder in which the model is placed:

- A user can **see** a model if they hold any permission on the model's folder.
- Which options are offered for opening the model depends on the permission types the user holds: **BrowserBasedViewing** for opening it in the browser, **Streaming** for opening it in the local UDiTH application, **Cache** for downloading it for offline use.
- A user can **modify or delete** a model if they have the **Browser Maintainer** role.
- **Model import** is restricted to users with the **Admin** role.

## Testing Permissions

Portal provides an **Open Access test page** under **Administration → Access** that allows administrators to verify the effective permissions for each user. Use this page to confirm that folder and model access is configured as expected before rolling out changes.

## User Management

Users are managed through Keycloak and synchronised to Portal. The synchronisation happens automatically when users sign in, or it can be triggered manually through the administration interface using **Force Synchronize**.

In the Portal administration interface at **Administration → Users**, administrators can:

- Create new users.
- Reset user passwords.
- View all synchronised users.
- Assign or remove roles.
- Assign users to groups.
- Delete users from Portal.

> **Note:** Users can also be created and managed directly in Keycloak. Changes are synchronised to Portal when users sign in or when **Force Synchronize** is triggered.

---

# UDiTH Portal — UI Settings

# UI Settings

The UI Settings page allows administrators to customise the appearance and behaviour of the Portal. It is accessible under **Administration → Settings**.

## General Settings

| Setting | Description |
|---------|-------------|
| **Email** | Contact email address displayed to users. |
| **Email Alias** | Alternative display text for the contact email. |
| **Phone** | Contact phone number displayed to users. |
| **Help URL** | URL to a help page or documentation site. Shown as a link in the Portal. |
| **Title** | Content title displayed on the Portal home page. Supports HTML formatting. This is different from the browser tab title, which is set via the environment variable `FrontendSettings__Title` (default: `UDiTH Portal`). |
| **Description** | Description displayed on the Portal home page. Supports HTML formatting. |

## Links

Custom links can be added to the Portal. Each link has a **Display Name** and a **URL**. Links are displayed in the Portal interface for quick access to external resources.

Use the **Add** button to create new links and the remove button to delete existing ones.

## Other Settings

| Setting | Description |
|---------|-------------|
| **Include models of subfolders** | When enabled, the model library shows models from subfolders in a collapsed view by default. |
| **Group PID search results by drawing name** | When enabled, PID search results are grouped by their drawing name. |

## Translations

Portal supports multiple languages. The translations section allows administrators to customise the text displayed in the Portal.

- **Download translations** — Downloads the current translation file as an `.xlsx` spreadsheet.
- **Overwrite translations** — Upload a modified `.xlsx` file to overwrite the Portal translations.

This allows you to adapt labels, messages, and other text to your organisation's terminology.

## Resources

The resources section allows you to customise the images used in the Portal. Navigate to **Administration → Settings → Resources** to manage the following:

| Resource | Description |
|----------|-------------|
| **Logo** | The logo displayed in the Portal navigation bar. |
| **Favicon** | The browser tab icon. |
| **Default Model Preview** | The default preview image shown for models that do not have a custom preview. |

Each resource can be replaced by uploading a new image, cleared to hide it, or reset to the built-in default.

## Themes

Portal includes multiple colour themes. If theme selection is enabled, users can switch between themes using the theme selector in the navigation bar. The user's preference is stored locally in the browser.

Available themes:

| Theme | Description |
|-------|-------------|
| **light** | Light colour scheme (default). |
| **dark** | Dark colour scheme. |
| **vienna** | Custom theme. |
| **scottsdale** | Custom theme. |

The default theme and available themes can be configured via the server-side frontend settings (`FrontendSettings__DefaultTheme`, `FrontendSettings__AvailableThemes`, `FrontendSettings__EnableThemeSelect`).

---

# UDiTH Portal — Observability

# Observability

UDiTH Portal and the render server export **metrics** and **log output** over
OpenTelemetry (OTLP). This page describes how to enable the export and documents
every metric the two components publish.

Distributed traces are not exported.

## Enabling the export

Both components read their OpenTelemetry configuration from the `OpenTelemetry`
section. Setting an endpoint is sufficient; everything else has a default. See
[Portal Configuration](/UDiTH%20Portal/Portal%20Configuration) for the full list
of keys and for how to provide them on each platform.

Portal on Linux (Docker):

```env
OpenTelemetry__Endpoint=https://otlp.example.com
OpenTelemetry__Protocol=http/protobuf
OpenTelemetry__ServiceName=udith-portal
OpenTelemetry__ResourceAttributes=deployment.environment=production
OpenTelemetry__MetricExportInterval=30000
OpenTelemetry__Headers_file=/run/secrets/otlp_authorization
```

Portal on Windows (`Settings/sharedsettings.json`):

```json
{
  "OpenTelemetry": {
    "Endpoint": "https://otlp.example.com",
    "Protocol": "http/protobuf",
    "ServiceName": "udith-portal",
    "ResourceAttributes": "deployment.environment=production",
    "MetricExportInterval": 30000,
    "Headers": "Authorization=Bearer <token>"
  }
}
```

The render server reads the same section from its own `appsettings.json`. Give
each render server its own `ServiceName`, or rely on the `WatchdogID` attribute
described below to tell them apart.

Notes:

- Setting `OpenTelemetry:Endpoint` also forwards the log output to the same
  endpoint. A separate Serilog sink is not required.
- `OpenTelemetry:Headers` usually contains a token. It can be supplied from a
  Docker secret file or from Azure Key Vault like any other setting.
- Without an endpoint, the OpenTelemetry SDK falls back to its own default
  (`localhost:4317`). Always set the endpoint explicitly.
- `MetricExportInterval` is the time in milliseconds between two exports. It also
  determines how often the values that are read on demand (disk space, hardware
  readings) are sampled.

## What is exported

| Meter                          | Component     | Contents                                          |
| ------------------------------ | ------------- | ------------------------------------------------- |
| `portal`                       | Portal        | Model access, imports, sessions, storage          |
| `watchdog`                      | Render server | Viewer instances, streams, host hardware          |
| `Microsoft.AspNetCore.Hosting` | Portal        | Standard ASP.NET Core request metrics             |
| `Microsoft.AspNetCore.*`       | Portal        | Standard ASP.NET Core routing and Kestrel metrics |
| `System.Net.Http`              | Portal        | Standard outgoing HTTP client metrics             |
| `System.Runtime`               | Portal        | Standard .NET runtime metrics (GC, threads, JIT)  |

The four standard meters are published by .NET and ASP.NET Core, not by Portal.
Their metrics are documented under
[Built-in metrics in .NET](https://learn.microsoft.com/dotnet/core/diagnostics/built-in-metrics).
The render server exports only the `watchdog` meter.

Instrument types used below:

| Type              | Meaning                                                                  |
| ----------------- | ------------------------------------------------------------------------ |
| `Counter`         | Monotonically increasing total                                            |
| `Gauge`           | Current value, written by the component when it changes                   |
| `ObservableGauge` | Current value, read by the component each time metrics are exported       |
| `Histogram`       | Distribution of recorded values                                           |

A missing unit means the value is a plain count.

## Portal metrics

### Model file access

Recorded while a client downloads model files for browser-based viewing or local
caching.

| Name                                            | Instrument | Unit | Attributes    | Description                                                        |
| ----------------------------------------------- | ---------- | ---- | ------------- | ------------------------------------------------------------------ |
| `portal.cmr.files.requested.count`              | Counter    |      | model version | Files requested                                                     |
| `portal.cmr.files.success.count`                | Counter    |      | model version | Files returned successfully                                         |
| `portal.cmr.files.notfound.count`               | Counter    |      | model version | Files requested but not found                                       |
| `portal.cmr.files.downloaded.bytes`             | Counter    | `By` | model version | Bytes returned to clients                                           |
| `portal.cmr.files.request.duration.milliseconds`| Histogram  | `ms` | model version | Time to retrieve a file. Only recorded for successful requests       |

### ZIP archive pool

A model version is stored as an archive. Portal keeps a pool of open archive
handlers per file so that concurrent requests do not have to reopen it. See
`ModelHosting` in [Portal Configuration](/UDiTH%20Portal/Portal%20Configuration).

| Name                                             | Instrument | Unit | Attributes | Description                                     |
| ------------------------------------------------ | ---------- | ---- | ---------- | ----------------------------------------------- |
| `portal.cmr.storage.zip.created.count`           | Counter    |      | `FilePath` | Archive handlers opened                          |
| `portal.cmr.storage.zip.destroyed.count`         | Counter    |      | `FilePath` | Archive handlers closed                          |
| `portal.cmr.storage.zip.available.count`         | Gauge      |      | `FilePath` | Handlers currently free in the pool              |
| `portal.cmr.storage.zip.acquire.milliseconds`    | Histogram  | `ms` | `FilePath` | Time spent waiting for a free handler            |
| `portal.cmr.files.initiate.archive.milliseconds` | Histogram  | `ms` | `FilePath` | Time to open a new archive handler               |

A rising `zip.acquire.milliseconds` together with `zip.available.count` at zero
means the pool is too small for the load; raise
`ModelHosting:ZipArchivePoolMaxSize`.

### Model import

One import run walks through the states below. A single import produces one
`recognized` measurement and then exactly one of `invalid`, `failed` or
`completed`.

| Name                                        | Instrument | Unit | Attributes    | Description                                            |
| ------------------------------------------- | ---------- | ---- | ------------- | ------------------------------------------------------ |
| `portal.cmr.model.import.recognized.count`  | Counter    |      | model version | Imports recognised and started                          |
| `portal.cmr.model.import.invalid.count`     | Counter    |      | model version | Imports rejected during pre-validation as invalid       |
| `portal.cmr.model.import.approved.count`    | Counter    |      | model version | Imports approved for processing                         |
| `portal.cmr.model.import.completed.count`   | Counter    |      | model version | Imports completed successfully                          |
| `portal.cmr.model.import.failed.count`      | Counter    |      | model version | Imports that failed                                     |

Early in an import the model is not yet identified. Those measurements carry
`model.id = 0` and an empty `model_version.uid`.

### Models, versions and sessions

| Name                                       | Instrument      | Unit | Attributes    | Description                                                           |
| ------------------------------------------ | --------------- | ---- | ------------- | --------------------------------------------------------------------- |
| `portal.cmr.model.version.count`           | Gauge           |      | model         | Number of versions per model                                           |
| `portal.cmr.model.version.deletion.count`  | Counter         |      | model version | Model versions deleted                                                 |
| `portal.cmr.model.sessions.open.count`     | Gauge           |      | model version | Currently open viewing sessions                                        |
| `portal.cmr.preload.active.count`          | Gauge           |      | model version | Preloaded viewer instances that are running but not yet claimed         |
| `portal.cmr.model.version.storage.bytes`   | ObservableGauge | `By` | model version | Disk space occupied by the model version                               |

Use `portal.cmr.model.version.count` together with
`ModelProcessing:MaxRetainedModelVersions` to see whether automatic cleanup is
keeping up.

### Server

| Name                                        | Instrument      | Unit | Attributes   | Description                                          |
| ------------------------------------------- | --------------- | ---- | ------------ | ---------------------------------------------------- |
| `portal.signaling.watchdogs.connected.count` | Gauge           |      | *none*       | Render servers currently connected to Portal          |
| `portal.disk.total.size.bytes`              | ObservableGauge | `By` | `drive.name` | Total capacity of each fixed drive                    |
| `portal.disk.available.space.bytes`         | ObservableGauge | `By` | `drive.name` | Free space on each fixed drive                        |

`portal.signaling.watchdogs.connected.count` dropping below the number of
configured render servers is the earliest sign that browser-based viewing
capacity is reduced.

## Render server metrics

Every metric in the `watchdog` meter carries the `WatchdogID` attribute, so a
single dashboard can cover all render servers.

### Viewer instances

| Name                                                  | Instrument | Unit | Attributes                | Description                                         |
| ----------------------------------------------------- | ---------- | ---- | ------------------------- | --------------------------------------------------- |
| `watchdog.viewer.start.succeeded.count`               | Counter    |      | `Model`                   | Viewer instances started successfully                |
| `watchdog.viewer.start.failed.count`                  | Counter    |      | `Model`                   | Viewer instances that failed to start or timed out   |
| `watchdog.viewer.signaling.connection.succeeded.count`| Counter    |      | `Model`                   | Viewer instances that reached Portal                 |
| `watchdog.viewer.kill.count`                          | Counter    |      | *none*                    | Viewer instances terminated by the render server     |
| `watchdog.viewer.start.duration.milliseconds`         | Histogram  | `ms` | `Model`                   | Time from launch to a usable viewer                  |
| `watchdog.viewer.active.count`                        | Gauge      |      | `Model`, `ModelVersionUid`| Viewer instances currently running                   |
| `watchdog.viewer.idle.count`                          | Gauge      |      | *none*                    | Running instances available for a new session        |
| `watchdog.viewer.launch.queue.count`                  | Gauge      |      | *none*                    | Instances waiting in the launch queue                |
| `watchdog.viewer.not.responding.count`                | Gauge      |      | *none*                    | Instances that stopped answering health checks       |

A permanently non-zero `watchdog.viewer.launch.queue.count` means the host cannot
start instances as fast as they are requested. A rising
`watchdog.viewer.not.responding.count` usually points at the graphics driver or
at memory pressure on the host.

### Streams

A stream is one browser connected to one viewer instance.

| Name                                   | Instrument | Unit | Attributes                 | Description                        |
| -------------------------------------- | ---------- | ---- | -------------------------- | ---------------------------------- |
| `watchdog.stream.started.count`        | Counter    |      | `Model`, `ModelVersionUid` | Streams that became active          |
| `watchdog.stream.stopped.count`        | Counter    |      | `Model`, `ModelVersionUid` | Streams that ended                  |
| `watchdog.stream.active.count`         | Gauge      |      | *none*                     | Streams currently active            |
| `watchdog.stream.duration.milliseconds`| Histogram  | `ms` | `Model`, `ModelVersionUid` | How long a stream lasted            |

### Connection to Portal

| Name                                                | Instrument | Unit | Attributes | Description                                                             |
| --------------------------------------------------- | ---------- | ---- | ---------- | ----------------------------------------------------------------------- |
| `watchdog.webservices.events.received.start.count`  | Counter    |      | *none*     | Start requests received from Portal                                      |
| `watchdog.webservices.events.received.stop.count`   | Counter    |      | *none*     | Stop requests received from Portal                                       |
| `watchdog.webservices.events.received.ignored.count`| Counter    |      | *none*     | Requests ignored because no matching instance existed or they arrived out of order |
| `watchdog.client.events.start.connection.count`     | Counter    |      | *none*     | Client connection attempts initiated by the render server                |
| `watchdog.hub.reconnect.count`                      | Counter    |      | *none*     | Times the connection to Portal was re-established                        |

Repeated `watchdog.hub.reconnect.count` increases indicate an unstable network
path or a reverse proxy closing idle connections; check the WebSocket timeout of
the proxy in front of Portal.

### Host hardware

| Name                                    | Instrument      | Unit  | Attributes              | Description                       |
| --------------------------------------- | --------------- | ----- | ----------------------- | --------------------------------- |
| `watchdog.cpu.load.percent`             | ObservableGauge | `%`   | `cpu.name`              | CPU load per physical CPU          |
| `watchdog.ram.used.bytes`               | ObservableGauge | `By`  | `memory.name`           | Memory in use                      |
| `watchdog.ram.available.bytes`          | ObservableGauge | `By`  | `memory.name`           | Memory available                   |
| `watchdog.gpu.utilization.percent`      | ObservableGauge | `%`   | `gpu.uuid`, `gpu.name`  | GPU utilisation                    |
| `watchdog.gpu.memory.used.megabytes`    | ObservableGauge | `MB`  | `gpu.uuid`, `gpu.name`  | GPU memory in use                  |
| `watchdog.gpu.temperature.celsius`      | ObservableGauge | `Cel` | `gpu.uuid`, `gpu.name`  | GPU temperature                    |
| `watchdog.disk.total.size.bytes`        | ObservableGauge | `By`  | `drive.name`            | Total capacity of each fixed drive |
| `watchdog.disk.available.space.bytes`   | ObservableGauge | `By`  | `drive.name`            | Free space on each fixed drive     |

Availability:

- CPU and memory metrics are only exported on **Windows** render servers, and
  only while `EnableHardwareMonitoring` is `true`.
- GPU metrics require an NVIDIA GPU and `nvidia-smi` on the host. They are not
  exported when the polling interval `NvidiaSmiCallInterval` is set to `0`.
- Disk metrics cover fixed drives only; network shares are not reported.

## Attributes

| Attribute                            | Component     | Description                                                 |
| ------------------------------------ | ------------- | ----------------------------------------------------------- |
| `model.id`                           | Portal        | Internal numeric model id. `0` when not yet known            |
| `model.name`                         | Portal        | Model name                                                   |
| `model.external_identifier`          | Portal        | External model identifier                                    |
| `model_version.uid`                  | Portal        | Model version UID. Empty when not yet known                  |
| `model_version.external_identifier`  | Portal        | External model version identifier                            |
| `FilePath`                           | Portal        | Path of the model archive an archive handler belongs to      |
| `drive.name`                         | both          | Drive or mount point, for example `C:` or `/`                |
| `WatchdogID`                         | Render server | Identifier of the render server, from its `WatchdogId` setting |
| `Model`                              | Render server | Path of the model the viewer instance was started for        |
| `ModelVersionUid`                    | Render server | Model version UID                                            |
| `cpu.name`                           | Render server | Name of the physical CPU as reported by the host             |
| `memory.name`                        | Render server | Name of the memory sensor as reported by the host            |
| `gpu.uuid`                           | Render server | GPU UUID reported by `nvidia-smi`                            |
| `gpu.name`                           | Render server | GPU model name reported by `nvidia-smi`                      |

In the tables above, **model version** stands for the five model and model
version attributes together, and **model** for `model.id`, `model.name` and
`model.external_identifier`.

Attribute values such as model names and drive letters are chosen by the
installation. Keep the number of distinct models under control, because each
combination of attribute values becomes a separate time series in the monitoring
backend.

## Log output

With `OpenTelemetry:Endpoint` set, log records are sent to the same endpoint as
the metrics, in addition to the console and file sinks. Log levels are controlled
through the `Serilog` settings, independently of the metrics configuration; see
[Portal Configuration](/UDiTH%20Portal/Portal%20Configuration).

Log records for HTTP requests and viewing sessions contain user names only when
`ShowPII` is enabled.

---

# UDiTH Portal — Using federated accounts

# Using federated accounts

You can configure UDiTH Portal account management to use an external authentication provider, for example ADFS, through one of the authentication protocols supported by Keycloak.
This delegates authentication to the selected identity provider.

This document shows the configuration process by using Microsoft Azure as an example.

## Basic configuration

1. Create a new **Identity provider** entry in the Keycloak administration UI.

	- Select the application realm, default: **uws**.
	- Go to **Configure / Identity providers**.
	- Add a new **User-defined** provider: **OpenID Connect v1.0**.

	<table>
	<tr><th>Field</th><th>Description</th></tr>
	<tr><td>

	Alias

	</td><td>

	Identifies the provider. This value can be chosen freely.

	</td></tr>
	<tr><td>

	Discovery endpoint

	</td><td>

	Path to the `.well-known/openid-configuration`, for example for Azure ADFS: `https://login.microsoftonline.com/<tenant id>/v2.0/.well-known/openid-configuration`

	</td></tr>
	<tr><td>

	Client ID

	</td><td>

	Obtained from the Azure app registration.

	</td></tr>
	<tr><td>

	Client Secret

	</td><td>

	Can be generated in the Azure app registration under **Manage / Certificates & secrets**.

	</td></tr>
	<tr><td>

	Advanced/Backchannel logout (optional)

	</td><td>

	Recommended value: **true**. This ensures that logging out of UDiTH Portal does not also log the user out of the identity provider.

	</td></tr>
	</table>

	<img src="./media/keycloak_add_identity_provider.png" width="789" height="484">

2. Set up a new app registration in Microsoft Azure.

	The **Redirect URI** can be copied from the first field entry in the Keycloak registration, for example `http://localhost:8080/realms/uws/broker/oidc/endpoint`. Enter it in the **Redirect URI** field of the Azure app registration.

	![](./media/oidc2.png)

3. Configure API permissions.

	On the Azure API permissions page, add `email`, `openid`, `profile`, and `User.Read` as delegated Microsoft Graph permissions.

	![](./media/oidc2-permissions.png)

4. Try logging in to UDiTH Portal.

	There should now be a new sign-in option in the Keycloak login screen.

## Additional configuration options

### Use group synchronization

Allow UDiTH Portal to extract user groups from the external provider. The user groups are synchronised when UDiTH Portal is restarted or when **ForceRefresh** is called in the Admin UI.

Create the groups to be synchronised in UDiTH Portal at `../administration/groups`.
Assign the **Group GUID** found in Azure Portal to the new group. The group connection is established by using this GUID value.

<img src="./media/UPVWebServices_oidc_group_guid.png" width="691" height="220">

Allow the group claim to be requested in Azure Portal:

On the **Token Configuration** page:

- add an additional `groups` claim.

<img src="./media/UPVWebServices_oidc_groupclaim.png" width="764" height="303">

Relevant for Keycloak major version 25 and later:

Create the `groups` attribute, as unmanaged attributes are no longer allowed by default.
To do this, go to **Realm Settings / User profile** and create an attribute named **groups**.

In the Keycloak **Identity provider** settings, add a new entry under **Mappers**.
This transmits the information from the **groups** claim to the user's attribute field. UDiTH Portal expects the **User attribute name** to be set to **groups**.
If your identity provider uses a different group claim name, you can define the required **Claim** name in this mapper. Note that the **Claim** value is case-sensitive.

<img src="./media/keycloak_add_mapper.png" width="789" height="484">

**Sync mode** defines when the mapper is executed.

Options:

- **Force**: Updates on every login. Recommended.
- **Import**: Updates only when the user is created in Keycloak for the first time.

### Import roles

You can also use a new **Mappers** entry for this.

This is done by using the **Claim to Role** mapper.

### Metadata customization

You can also use a new **Mappers** entry for this.

This is done by providing the required **User attributes**. Relevant predefined mappers for this use case are **Attribute Importer** and **Hardcoded Attribute**.
For example, if you provide a user attribute named **email**, this corresponds to the user's email entry.

<img src="./media/keycloak_add_mapper_attribute.png" width="789" height="484">

Keycloak claims:

- username
- firstName
- lastName
- email
- emailVerified, which does not appear to work at the moment

### Troubleshooting

You can use the following Keycloak start command to enable logging of user claims during sign-in:

`kc.bat start --log-level="INFO,org.keycloak.social.user_profile_dump:debug"`

---

# UDiTH Portal — Advanced integration options

# Advanced integration options

There are multiple flexible options for integrating your product with UDiTH Portal.

Code examples are available at:

<https://github.com/caxperts/upv-webservices-integration>

## Authentication

For specific use cases, it is possible to handle OpenID Connect authentication directly and use its wide range of options.

The BBV WebRTC integration package supports setting an access token directly without using the integrated automatic sign-in.
This is demonstrated in the login popup flow here:

<https://github.com/caxperts/upv-webservices-integration/tree/main/browserbasedviewing-api-samples/webrtc-integration-sample>

By calling `setAccessTokenCall`, you can pass the token directly. The original part of the sample relating to pop-ups and account handling is not required for this use case.
Please note that the token must be updated when it expires. The JWT token contains a lifetime property.

```typescript
token = 'eyJhbGciOiJSUzI1NiIsInR5cCI...'; // a valid OIDC access token

private connect() {
  this.upvApi = new UpvWebInterface(this.signalingServerBaseUrl + 'signaling');

  // Provide a function that returns the current access token
  this.upvApi.setAccessTokenCall(() => this.token);

  window.addEventListener(this.upvApi.connectedEvent, () => {
    // API now available
    let command = this.upvApi.createApiCommand("Select", null, null, "Task=Equipment", null, 1);
    this.upvApi.sendApiCommand(command, r => console.log('result', r));
  });

  this.upvApi.connect("http://demo.universalplantviewer.com/demoPlant/8/0", 'displayname', this.player.nativeElement);
}
```

> A complete, runnable version of this example is available in the [integration samples repository](https://github.com/caxperts/upv-webservices-integration/tree/main/browserbasedviewing-api-samples/webrtc-integration-sample).

By using this approach, it is possible to customise the product extensively.

### Example: Service accounts

By using Keycloak service accounts, it is possible to request authentication for a specific user without the regular user sign-in flow.
This is commonly used for machine-to-machine scenarios.

An access token can be obtained by using the standard OpenID Connect **Client Credentials Grant** flow.

### Access control

Clients that use the **Client Credentials Grant** automatically have a service account user created in Keycloak.
For example, for the client `example-client`, a user named `service-account-example-client` is created.

<img src="./media/UPVWebServices_advanced_accesscontrol.png" width="789" height="484">

After calling `ForceSynchronize` on the UDiTH Portal application side, this account is also available in the UDiTH Portal admin UI.
Here, you can configure the fine-grained access rights for the account.

<img src="./media/UPVWebServices_advanced_usermanagement.png" width="789" height="484">

The user group can then be assigned permissions for specific models.

<img src="./media/UPVWebServices_advanced_permission.png" width="789" height="484">

#### How to create a service account

- Open the Keycloak URL.
- Go to **Administration Console**.
- Sign in by using a Keycloak admin account. If you are not self-hosting the Keycloak server, contact your hosting provider and ask them to provide an admin account.
<img src="./media/service_account_1.png" width="789" height="484">
  - Make sure that your application realm is selected. The realm name is configurable during installation. The `master` realm must never be used. When using a hosting provider, this realm may be named differently.
  - Go to **Clients** in the left-hand menu. Service accounts are directly associated with an OpenID Connect client entry.

- Click **Create client**.
- Choose any value for **Client ID** and click **Next**.

<img src="./media/service_account_2.png" width="789" height="484">

- On the **Capability config** step, apply the following settings (shown in the image above):
  - **Client authentication**: **On**
  - **Authorization**: **Off**
  - **Authentication flow**: enable **Standard flow**, **Direct access grants**, and — most importantly — **Service accounts roles**.

  The **Service accounts roles** option is responsible for associating a new service account with the client and must be enabled for machine-to-machine authentication. You can, of course, change these settings if you have specific authentication requirements.
- Click **Next**.
- Leave **Login settings** unchanged and save the client.
- The client automatically receives a client secret, that is, a password. You can access it on the **Credentials** tab of the client.
- The generated Keycloak service account can be viewed on the client page under the **Service accounts roles** tab.

---

# UDiTH Portal — Secure Setup Guidelines

# Secure Setup Guidelines

This page describes the recommended way to operate UDiTH Portal with a small
attack surface. It is a hardening guide, not an installation guide. For the
installation steps, see [Setup Portal Linux](/UDiTH%20Portal/Setup%20Portal%20Linux)
and [Requirements](/UDiTH%20Portal/Requirements).

## Summary

| Topic            | Recommendation                                              |
| ---------------- | ----------------------------------------------------------- |
| Platform         | Linux with Docker (or Podman)                                |
| Image variant    | `ubuntu-chiseled`                                            |
| Isolation        | `cap_drop: ALL`, `no-new-privileges`, AppArmor profile        |
| Transport        | HTTPS only, legacy TLS protocols switched off                 |
| External access  | Reverse proxy, Caddy or the product of your choice           |
| Secrets          | Azure Key Vault, or Docker secrets                           |
| Maintenance      | Keep the host and all applications up to date                |

## Use Linux

Run Portal on Linux in containers. A container gives you process, file system
and network isolation that the Windows installation on IIS cannot provide.

- Use a supported LTS distribution.
- Install only the packages that the container runtime needs.
- Keep the Docker daemon and the container runtime current.

## Use the `ubuntu-chiseled` Image

The `quay.io/caxperts/portal` image is published in several variants. For a
secure setup, use `ubuntu-chiseled`.

- The chiseled image is a minimal, distroless-style image. It contains **no
  shell and no package manager**.
- Without a shell, an attacker who reaches the container has almost no tools
  available.
- The image contains fewer packages, so it has fewer known vulnerabilities.
- The container runs as the unprivileged user with uid `1654`, not as `root`.

The other variants (`ubuntu`, `alpine`, `noble`) run as `root` inside the
container. Use them only if you must run commands in the container, for example
for support or diagnostics.

Prepare the bind-mounted volumes for uid `1654`:

```bash
mkdir -p /srv/udith/storage /srv/udith/import /srv/udith/upload
chown -R 1654:1654 /srv/udith/storage /srv/udith/import /srv/udith/upload
chmod -R u+rwX /srv/udith/storage /srv/udith/import /srv/udith/upload
```

For details on the variants, the uids and the volume permissions, see
[Image Variants and Volume Permissions](/UDiTH%20Portal/Setup%20Portal%20Linux#image-variants-and-volume-permissions).
To add your own CA certificates to the chiseled image, see
[Adding Custom CA Certificates](/UDiTH%20Portal/Adding%20Custom%20CA%20Certificates).

### Additional Container Settings

Add the following settings to the Portal service in your `docker-compose.yml`:

```yml
    read_only: true
    tmpfs:
      - /tmp
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true
```

Notes:

- `read_only` requires that all writable paths are volumes or `tmpfs`. Test this
  setting before you use it in production.
- Do not publish the container port to the host. Let the reverse proxy reach the
  container over an internal Docker network.

### AppArmor Profile

CAXperts provides an AppArmor profile for the Portal container. The profile
restricts the container to the files, directories and network operations that
Portal actually needs.

> ⚠️ **Important**: The profile is only valid for the `ubuntu-chiseled` variant.
> The other variants have a different file system layout and a different user,
> so the profile does not match them.

Save the profile as `/etc/apparmor.d/docker-portal` on the host:

```
# Last Modified: Tue Jul  7 17:08:13 2026
abi <abi/4.0>,

include <tunables/global>

profile docker-portal flags=(attach_disconnected, complain, mediate_deleted) {
  include <abstractions/base>
  include <abstractions/ssl_certs>
  include <abstractions/user-tmp>

  network inet dgram,
  network inet stream,
  network inet6 dgram,
  network inet6 stream,
  network netlink raw,

  deny mount,
  deny umount,

  mqueue getattr type=posix,

  deny ptrace trace,

  deny /proc/kcore r,
  deny /proc/sys/kernel/** wlkx,
  deny /sys/firmware/** rwlkx,
  deny /sys/fs/cgroup/** wlkx,
  deny /sys/kernel/security/** rwlkx,

  /app/Portal/{,**} rk,
  /etc/resolv.conf rk,
  /etc/{host.conf,hosts,passwd} r,
  /proc/*/net/{ipv6_route,route} rk,
  /proc/sys/net/ipv4/conf/*/forwarding rk,
  /proc/{cpuinfo,meminfo} rk,
  /run/secrets/* rk,
  /sys/devices/system/cpu/** r,
  /sys/fs/cgroup/** r,
  /usr/share/dotnet/** mrk,
  /{import,storage,upload}/{,**} rwk,
  /{usr/,}lib{,32,64}/** rk,
  owner /home/app/.aspnet/{,**} rwk,
  owner /proc/*/task/ r,
  owner /proc/*/task/*/comm rw,
  owner /proc/*/task/*/stat rk,
  owner /proc/*/{cgroup,cmdline,mountinfo,mounts,stat,status} rk,

}
```

Load the profile and confirm that it is active:

```bash
sudo apparmor_parser -r /etc/apparmor.d/docker-portal
sudo aa-status | grep docker-portal
```

Assign the profile to the Portal service in your `docker-compose.yml`:

```yml
    security_opt:
      - no-new-privileges:true
      - apparmor=docker-portal
```

Recreate the container to apply the profile.

The profile is delivered with the `complain` flag. In this mode AppArmor allows
every operation and only logs the violations. Use this mode first, and check the
log for denials:

```bash
sudo journalctl -k | grep -i apparmor
```

When no denial appears during normal operation, switch the profile to enforce
mode. Remove `complain` from the `flags=(...)` list, or run:

```bash
sudo aa-enforce /etc/apparmor.d/docker-portal
```

Notes:

- The profile must be loaded on the **host**. The container itself contains no
  AppArmor configuration.
- The paths `/import`, `/storage` and `/upload` are the only writable mounts.
  If you mount additional volumes, add them to the profile.
- The profile does not replace the other container settings. Use it together
  with `cap_drop`, `no-new-privileges` and the non-root user.

## Use a Reverse Proxy

Do not expose Portal or Keycloak directly to the internet. Put a reverse proxy in front of
it. The proxy terminates TLS, provides the certificate and hides the
application.

We recommend [Caddy](https://caddyserver.com/), because it requests and renews
certificates automatically and uses secure TLS defaults. Any other reverse proxy
is also supported. Use the product that your organisation already operates, for
example NGINX, HAProxy, Traefik or a hardware appliance.

Requirements for the reverse proxy:

- Terminate HTTPS with a trusted certificate for the Portal domain.
- Forward `X-Forwarded-For`, `X-Forwarded-Proto` and `X-Forwarded-Host`.
- Allow WebSocket connections.
- Do not limit the upload size below the size of the models that your users
  upload.

### Sample `Caddyfile`

```caddyfile
portal.example.com {
	reverse_proxy portal_demo:8080
}

keycloak.example.com {
	reverse_proxy keycloak:8080
}
```

Caddy sets the `X-Forwarded-*` headers and handles WebSocket upgrades without
extra configuration. Replace the host names with the service names of your
Compose project, and confirm the container port of your Portal version before
you use this file.

Keycloak needs `KC_PROXY_HEADERS: xforwarded` and `KC_HOSTNAME` for operation
behind a proxy. See the Keycloak sample in
[Setup Portal Linux](/UDiTH%20Portal/Setup%20Portal%20Linux).

## TLS and Certificates

- Use a trusted certificate that meets current security standards. See the
  [Common requirements](/UDiTH%20Portal/Requirements#common-requirements)
  section.
- Switch off outdated or insecure legacy TLS protocols on every web server. This
  applies to the reverse proxy, to IIS on Windows and to Keycloak.
- Keep `IgnoreCertificateErrors` at the default value `false`. This setting
  accepts invalid TLS certificates for outgoing connections and removes the
  protection against a man-in-the-middle attack.
- Terminate TLS in the reverse proxy. Terminate TLS directly in Kestrel only if
  a fully encrypted connection to the container is required. See the
  [Portal Configuration](/UDiTH%20Portal/Portal%20Configuration) page.
- If your certificate comes from a company-internal Certificate Authority, the
  CA certificate must be trusted inside the container. See
  [Adding Custom CA Certificates](/UDiTH%20Portal/Adding%20Custom%20CA%20Certificates).

## Keycloak

Keycloak holds the identities of all users. Protect it at least as well as
Portal.

- Keep Keycloak up to date with the latest security fixes.
- Allow incoming traffic only on the port on which Keycloak runs.
- Behind a reverse proxy, set `KC_PROXY_HEADERS: xforwarded` and `KC_HOSTNAME`.
- Use a separate database and a separate database user for Keycloak.

## Windows Installations

The Linux container setup described above is the recommended deployment. If you
operate Portal on Windows with IIS, the following points apply.

- Keep Windows and IIS up to date with the latest security fixes.
- Switch off outdated or insecure legacy TLS protocols in the operating system
  and in IIS.
- HTTPS is required. A setup over HTTP is not supported.
- Protect `Settings/sharedsettings.json` in the installation directory. It holds
  the secrets. Restrict the NTFS permissions to the application pool identity
  and to the administrators.
- Read the secrets from an Azure Key Vault instead, as described above.
- Run the application pool with a dedicated service account that has the minimum
  permissions.

## Keep the Host and the Applications Up to Date

Updates are the most effective protection. Plan a regular maintenance window.

- Install the security updates of the operating system. Enable unattended
  security updates if your change process permits it.
- Pull the current Portal, CAXturn, Keycloak and MSSQL images and recreate the
  containers.
- Pin an explicit image tag instead of `latest`, so that you know which version
  runs. Change the tag when you update.
- Read [Breaking Changes](/UDiTH%20Portal/Breaking%20Changes) before each Portal
  update.
- Keep the render servers and the client workstations up to date.

## Load Secrets from Azure Key Vault

Do not keep secrets in the `docker-compose.yml` file, in an `.env` file or in
the environment of the container. Portal can read any setting from an Azure Key
Vault instead.

A secret name maps to a setting name. Replace each colon in the setting name
with a double dash. For example, the secret `ConnectionStrings--AdminConnection`
provides the setting `ConnectionStrings:AdminConnection`. Portal queries the
vault only when a vault URI is configured.

```yml
    environment:
      - AzureKeyVault__VaultUri=https://my-vault.vault.azure.net/
      - AzureKeyVault__Prefix=prod-
```

Recommendations:

- Use the ambient Azure identity, for example a managed identity. Leave
  `AzureKeyVault:TenantID`, `AzureKeyVault:ClientID` and
  `AzureKeyVault:ClientSecret` unset. No credential is then stored on the host.
- If a managed identity is not available, prefer a client certificate
  (`AzureKeyVault:ClientCertificatePath`) over a client secret.
- Give the identity only the **get** and **list** permissions for secrets.
- Use `AzureKeyVault:Prefix` to serve several environments from one vault.
- Enable soft delete, purge protection and the audit log of the vault.
- Rotate the secrets in the vault. Restart the container to apply new values.

For the full list of the Key Vault settings, see the **Azure Key Vault** section
on the [Portal Configuration](/UDiTH%20Portal/Portal%20Configuration) page.

## Further Recommendations

- If you cannot use a Key Vault, use Docker secrets. Any variable can be read
  from a file. Append the `_file` suffix to the variable name, for example
  `KeycloakAdmin__Password_file=/run/secrets/keycloak_password`.
- Open only the necessary ports in the firewall. See the
  [Ports](/UDiTH%20Portal/Requirements#ports) section.
- Do not publish the MSSQL port `1433` to the internet.
- Use a separate database user with the minimum permissions for Portal and for
  Keycloak.
- Enable the logs and metrics endpoints and monitor them. See
  [Observability](/UDiTH%20Portal/Observability).
- Restrict the permissions of the users in Portal. See
  [Permission Management](/UDiTH%20Portal/Permission%20Management).

---

# Toolkit App for UDiTH — Toolkit Administration Guide

# Toolkit Administration Guide

Welcome to the administration guide for UDiTH App Toolkit. This guide provides detailed instructions for managing the application efficiently.

# License

The **License Management** section allows administrators to manage the application's license key. This ensures compliance and enables access to application features. All license keys are granted by CAXperts.

## Features

### 1. License Key Input

- Provides a secure interface to input or update the license key.

- Dynamically toggles between masked and unmasked views for the license key by using the **Eye** icon to the right of the textbox.

![License input masking](media/admin_guide_license_management.gif)

### 2. Validation and Feedback

- Displays toast notifications to confirm successful updates.

- Ensures that the provided license key is valid and up-to-date.

- If the license key is invalid, an error notification is displayed, and the following screen is shown.

![Invalid License](media/admin_guide_invalid_license.png)

## License Management Actions

### View and Edit License Key

1. Navigate to the **License** page.

2. The current license key is preloaded in the input field upon page load.

3. To update the license key:

- Enter the new key in the text field.

- Use the **Eye** icon to toggle between masked and unmasked views of the key.

### Submit the License Key

1. After entering the new key, click the **Upload** button.

2. The system performs the following actions:

- Validates the license key.

- Updates the license key for the project.

- Displays a success notification confirming the update.

3. Upon successful update, you are redirected to the **Home** page.

# Roles

The **Roles** page serves to control which roles are available in the application. 
These roles are sourced from Keycloak and are used to populate dropdown selections within the system. This table does not configure user permissions but user responsibilities e.g. used in work flows.

![The roles page](media/admin_guide_roles.png)

### Tip

- Double-check role edits before pressing Enter to avoid accidental changes.



## Keycloak User Synchronization

The Toolkit includes an automated synchronization system that keeps user accounts and their assigned roles in sync between **Keycloak** (the identity provider) and the **application database**.

### How the Synchronization Works

- **Keycloak is the Source of Truth**: The synchronization process treats Keycloak as the authoritative source for user roles and group assignments.

- **Automated Sync**: By default, the background worker runs the synchronization job **every 30 minutes**.

- **What Gets Synchronized**:
  - All users with email addresses in Keycloak are processed
  - User group memberships in Keycloak are compared with role assignments in the database
  - Missing roles are **added** to the database
  - Extra roles (present in database but not in Keycloak) are **removed**
  - User accounts are created or updated as needed

### Configuration

The synchronization behavior can be customized using environment variables in your deployment configuration:

#### Enable/Disable Synchronization

```
RUN_WORKER=true
```

Set this to `false` to disable the background worker (including user synchronization). If you leave it empty, the worker runs. The legacy name `ENABLE_CRON` is still read when `RUN_WORKER` is empty.

#### Customize Sync Schedule

```
Worker__KeycloakSyncIntervalMinutes=30
```

This variable sets the number of minutes between two synchronizations. The default is `30`.

**Note**: The legacy variable `KC_GROUP_SYNC_CRON` is only read in the form `*/N * * * *` (every N minutes). The system ignores all other cron expressions (for example `0 3 * * *`), logs a warning at start, and uses the 30-minute default.

# Workflows

The **Workflows** page allows administrators to create, view, edit, delete and assign default workflows.

![The workflows administration page](media/admin_guide_workflows_page.png)

## Overview

- Workflows are assigned to specific punch items. They are used to define the steps that must be completed to resolve a punch.

- The admin defines those steps and who is authorized to complete them.

- When the workflow is successfully finished (once the last step is approved), and if the last step contains a coded step closeWorkflow(), the punchlist item is closed.

### 1. Workflow Navigation

- The Workflow Management page displays a list of existing workflows and their details.

- Admins can add new workflows or edit existing ones.

![Navigating the workflow](media/admin_guide_navigating_workflow.gif)

### 2. Default Workflow Assignment

- The **Assign Default Workflow** feature lets administrators designate or reassign the workflow that is automatically applied to new punch items.

- This option is accessible by clicking on the Workflow Tools dropdown menu in the management pane.

![Using the assign default workflow feature](media/admin_guide_assign_default.gif)

### 3. Workflow Interface

- Administrators can create a new workflow by clicking the **Add Workflow** button.

- A popup prompts users to enter a unique workflow title.

![Adding a new workflow](media/admin_guide_add_workflow.gif)

- Once inside a new/existing workflow, the administrator can start adding steps using the **Plus** button in the top right of the page.

- Once a step is added the different fields in the row can be edited by double clicking in the respective cell.

- Step Number, Title, Description and Responsible Person are all mandatory fields and must be filled in otherwise an error will be thrown.

- After making any changes, the **Save** button in the top right will become active and display a red background. Administrators can save their modifications by clicking this button.

- The step number denotes the order in which the steps will need to be completed to successfully finish the workflow.

- The workflow step can have different use actions: "None", "Activity", "Decision".

  "None" does not require any interaction from a user.

  "Activity" requires the user to approve or disapprove the step with a button click.

  "Decision" requires the user to approve or disapprove with or without signature.

### 4. Workflow Columns

- **Steps Considered for Progress**: Defines which workflow steps are included when calculating progress after reaching a specific step. When a step (e.g., Step 5) is reached, the system compares the steps listed here with the steps completed so far. For example, if Step 5 can only be reached after Steps 1–4 and is the final step, enter `1,2,3,4,5`. This allows customizing progress for each workflow step, enabling non‑linear workflows. The field accepts comma‑separated numbers representing workflow steps.

- **Responsible Person** column specifies who is authorized to approve or deny the workflow step. The roles that are shown are roles that are specified by admin in Keycloak Groups.

- **Forward Step ID** column specifies what is the next workflow step when the pending step is approved.

- **Backward Step ID** column specifies what is the next workflow step when the pending step is disapproved.

> **Note:** The default behavior of the **Forward Step ID** and **Backward Step ID** columns can be overridden using [Custom Code](#custom-code) functions:
>
> - `stepForwardJumpTo(stepNumber: number, message: string | null)`: Jumps to the specified step number and, if a message is provided, it is printed to the activity chat. This jump is treated as a success.
> - `stepBackwardJumpTo(stepNumber: number, message: string | null)`: Jumps to the specified step number and, if a message is provided, it is printed to the activity chat. This jump is treated as a failure.
>
> When used, these functions take **priority over the Forward Step ID and Backward Step ID values** defined in the workflow table.
>
> A jump to the step's **own** number keeps the step pending: nothing is recorded and no new step is created. Combine it with `showPopup(message)` to tell the user why the step did not move on.

### 5. Workflow Code

Allows users to define [Custom Code](#custom-code) that is run when workflow step is accepted or disapproved.

Before saving the code, it must be compiled with a mockup interface that does not influence original punch items. The admin can choose different punches for examplary compilation if they specify a number in "PunchlistID for compilation" field.
If the compilation is successful the code can be saved.

> **Related Features:**
>
> - Use [Event Listeners](#event-listeners) to trigger actions based on workflow events (step acceptance, field changes, etc.)
> - Use [Field Validators](#field-validators) to enforce data validation rules before field values are saved

### 6. Workflow Email Message

Allows users to send an email with the provided html body to the specified responsible group in the workflow step.

**Available Dynamic Variables:**

- **Ticket Fields** - Include any punch field value:

  ```
  ${Details["<field name>"]}
  ```

  Example: `Pipe in Area System: ${Details["Area System"]} broken.`

- **Ticket ID** - Include the punch item ID:

  ```
  ${PunchItem}
  ```

  Example: `Ticket #${PunchItem} requires attention.`

- **Server Address** - Include the application server URL:

  ```
  ${serverAddress}
  ```

  Example: `View ticket at: ${serverAddress}/tickets/${PunchItem}`

- **Current Date** - Include formatted date using the Date() function:

  ```
  ${Date("format")}
  ```

  **Common Date Formats:**
  - `${Date("yyyy-MM-dd")}` → 2025-12-01
  - `${Date("MM/dd/yyyy")}` → 12/01/2025
  - `${Date("dd-MM-yyyy")}` → 01-12-2025
  - `${Date("yyyy-MM-dd HH:mm:ss")}` → 2025-12-01 14:30:00
  - `${Date("MMMM dd, yyyy")}` → December 01, 2025

  Example: `This notification was sent on ${Date("yyyy-MM-dd")}`

**Email Template Example:**

```html
<h2>Ticket Update Notification</h2>
<p>Dear Team,</p>
<p>Ticket <strong>#${PunchItem}</strong> has been updated.</p>
<ul>
  <li><strong>Status:</strong> ${Status}</li>
  <li><strong>Priority:</strong> ${Details["Priority"]}</li>
  <li><strong>Area System:</strong> ${Details["Area System"]}</li>
  <li><strong>Date:</strong> ${Date("MMMM dd, yyyy")}</li>
</ul>
<p>
  <a href="${serverAddress}/tickets/${PunchItem}"
    >Click here to view the ticket</a
  >
</p>
<p>This is an automated notification from ${serverAddress}</p>
```

**Note:** Subject lines can also use dynamic variable replacement with the same syntax shown above.

# Workflows Kanban

The **Kanban Settings** page allows administrators to configure the appearance of the Kanban cards that appear in the "**Planning**" section by selecting fields for the title, subtitle, and content zones. These settings impact how information is displayed within the Kanban view, enabling a tailored experience to suit specific workflows.

![The workflows kanban settings page](media/admin_guide_kanban.png)

## Kanban Settings Actions

- **Purpose**: Define what information appears on the Kanban cards for the title, subtitle, and content areas.

- **How to Use**:
  - Open the drop-down menus for **Title**, **Subtitle**, and **Content**.

  - Select a field from the list of available options, which are fetched from the system’s punch details available fields.

![Adjusting the fields](media/admin_guide_kanban_field_adjust.gif)

- Each selection updates the Kanban card preview in real time, allowing you to instantly see how your choices affect the card layout. The preview is displayed to the right of the select boxes.

- The values from the fields you select in the drop-down menus will automatically populate the corresponding sections of each individual Kanban card.

![Utilizing the drop-down menus](media/admin_guide_kanban_dropdown_menu.gif)

- After finalizing your selections, click the **Save** button.

- A confirmation message will appear, notifying you that the settings have been saved successfully.

# Ticket Fields

The **Ticket Fields** (also called Punchlist) page allows administrators to add, configure and manage available punchlist fields effectively. These fields define how information is structured and displayed in punchlists, ensuring adaptability to various workflows and requirements.

![Punchlist fields](media/admin_guide_punchlist_fields.png)

## Managing Ticket Fields

Administrators can add, edit, delete, and reorder punchlist fields using the following actions. This fields can also be grouped into ticket types. 

### Adding a New Ticket Field

1.  **Add a Field:**

- Click the **Plus (+) button** in the top right corner to add a new punchlist field.

2.  **Fill in Field Details:**

Fields are accessed by single-clicking on the respective cell.

- **Detail (Required):** Enter the name of the punchlist field.

- **Type (Required):** Select the field type. See options below.
  - **dxTextBox:** A textbox for user input of text data.
  - **dxSelectBox:** A dropdown select box allowing users to choose from predefined options. These options are specified by the administrator in the "**Items**" column.
  - **dxDateBox:** A date picker for selecting dates.
  - **dxHyperLink:** A hyperlink element that navigates to a user specified URL.
  - **Breakline:** Inserts a horizontal line to separate content sections in punchlist user form.

- **Additional Options:**

- **Column Span:** Specify the number of columns this field should occupy on the punch item's detail page.

- **Default View:** Choose whether this field should be displayed by default in the main punchlist table.

- **Items (for Selectbox):** If the type is Selectbox, specify the available options in the **Items** column.

- **Editors:** Grant the selected roles permission to edit the specified field. The field will be in a read-only state for roles not included in this option.

![Making a field read-only](media/admin_guide_editors_fields.gif)

3.  **Save or Revert:**

- **Save:** Click the **Save** button at the end of the row to save the new field.

- **Revert:** Click the **Revert** button to discard the new entry.

### Editing an Existing Ticket Field

- Click the **Pencil icon** at the end of the row corresponding to the field you wish to edit.

- Modify the necessary details.

- Click **Save** to apply changes or **Revert** to cancel.

### Deleting a Ticket Field

- Click the **Trash icon** at the end of the row for the field you want to delete.

- **Warning:** Deleted fields cannot be recovered. Confirm the deletion when prompted.

### Reordering Ticket Fields

- **Drag and Drop:**

- Click and hold the **Six-Dot icon** (⋮⋮) to the left of a row.

- Drag the row to your desired position within the table.

- Release to drop the row in the new position.
- The order of the rows in the table defines the order in which the fields appear in the punchlist user form.

![Re-ordering fields](media/admin_guide_punchlist_reordering_items.gif)

# Attribute Mapping

The **Attribute Mapping** section enables administrators to define relationships between ticket detail fields and model attributes. These mappings are applied to the comments of ticket items that are created using the "**Create with Comment**" option in main tickets table.

![The attributes mapping page](media/admin_guide_attribute_mapping.png)

## Features

### 1. Mapping Interface Overview

- **Punch Details List**: Displays un-mapped ticket details available for mapping.

- **Mapped Attributes List**: Lists existing mappings with details such as the mapped attribute(s) and type (2D, 3D, or Code).

### 2. Mapping Actions

**Add Detail to Mapping**

1. Click the "+" button to move the detail to the mapped attributes list.

2. The detail is now ready to be mapped to an attribute.

3. Select up to 3 ticket details to generate a custom ticket comment.

![Adding detail to mapping](media/admin_guide_mapping_adding_detail.gif)

**Map Attribute to Detail**

- In the **Mappings** pane click the **Edit** button of the field you would like to map an attribute to.

- This will open a pop-up that has options to either map to a 3D, 2D or custom code attribute.

- Click on any of these options to choose a mapping type:
  - **3D Single Mapping**: Map the field to a 3D attribute. A list is available for the administrator to select from which contains all possible model attributes.

  - **2D Single Mapping**: Map the field to a 2D attribute. These attributes can be user-defined in the textbox.

  - **Code Mapping**: Map the field using a [Custom Code](#custom-code)

- Save changes to finalize the mapping.

![Editing map attribute](media/admin_guide_mapping_editing_attribute.gif)

Creating a ticket with the option "Create with Comment" now reflect changes made with regards to attribute mapping:

![Create with Comment](media/admin_guide_createwithcomment.gif)

**Delete Mapping**

- Click the **Trash** button next to a mapped attribute to remove the mapping.

- The detail returns to the **Ticket Details** list for future use.

# Event Listeners

The **Event Listeners** section provides administrators with tools to manage event-driven automation throughout the Toolkit application. Event listeners enable custom actions or behaviors triggered by specific conditions, such as field value changes, workflow actions, ticket creation, email sending, or scheduled intervals.

Unlike [Field Validators](#field-validators) which **prevent** invalid data from being saved, event listeners **react** to events after they occur, executing custom code to automate workflows, send notifications, update related fields, or trigger external integrations.

![The event listener section](media/admin_guide_event_listeners.png)

## Understanding Listener Types

Each listener must have a **Type** that determines when and how it triggers. The listener's required fields vary based on the selected type. Administrators must select the appropriate type and configure type-specific fields to create functional listeners.

> **Important — a listener runs only once per ticket.** After a listener has run for a ticket, it does not run for that ticket again, even if its condition becomes true again later (for example, the same step is accepted a second time, or the ticket is overdue on the next check). The run is recorded even if the custom code fails. `onEmailReceived` and `onInteractionEmailSent` listeners are the exception: they run for every matching email.

### Available Listener Types

#### 1. onWorkflowStepAccepted

**Description:** Triggers when a specific workflow step is accepted by a user.

**When it triggers:** After a user approves/accepts a workflow step and the step status changes to "Accepted".

**Required Fields:**

- **Type**: `onWorkflowStepAccepted`
- **Checked Field**: Step number (e.g., "1", "2", "3")
- **Checked Value**: "Accepted"
- **Workflow ID**: The workflow this listener applies to
- **Custom Code**: JavaScript code to execute when triggered

**Use Cases:**

- Send notification when critical step is approved
- Auto-populate fields after approval
- Create comments or logs for audit trail

**Example:**

```
Type: onWorkflowStepAccepted
Checked Field: 3
Checked Value: Accepted
Workflow ID: 5
Custom Code: await sendMailTo(["manager@example.com"], "Step 3 approved", "Workflow Progress")
```

#### 2. onValueChanged

**Description:** Triggers when a specific punch detail field value changes to a specified value.

**When it triggers:** After a ticket field is updated and the new value matches the specified CheckedValue.

**Required Fields:**

- **Type**: `onValueChanged`
- **Checked Field**: Field name (e.g., "Status", "Priority")
- **Checked Value**: The value that triggers the listener (e.g., "Closed", "High")
- **Workflow ID** (or **Default**: true): Either specify a workflow or make it workflow-independent
- **Custom Code**: JavaScript code to execute when triggered

**Use Cases:**

- Notify team when status changes to "Closed"
- Auto-populate related fields when priority is set
- Trigger external API when specific conditions are met

**Example:**

```
Type: onValueChanged
Checked Field: Status
Checked Value: Closed
Default: false
Workflow ID: 5
Custom Code: await setTicketValue("ClosedDate", Date())
```

#### 3. onTicketCreated

**Description:** Triggers when a new ticket is created.

**When it triggers:** Immediately after a new ticket is created in the system.

**Required Fields:**

- **Type**: `onTicketCreated`
- **Default**: Typically set to `true` (apply to all new tickets)
- **Custom Code**: JavaScript code to execute when triggered (can include filtering logic)

**Use Cases:**

- Auto-assign new tickets based on criteria
- Set initial field values
- Send creation notifications
- Apply default workflows

**Example:**

```
Type: onTicketCreated
Default: true
Custom Code:
  const area = await getTicketValue("Area");
  if (area === "Engine Room") {
    await setTicketValue("AssignedTo", "engineering@example.com");
  }
```

#### 4. onInteractionEmailSent

**Description:** Triggers when an interaction email is sent from the support ticket system, filtered by regex patterns matching the email subject and/or body content.

**When it triggers:** After an email is sent successfully through the Interactions tab of a ticket, if the email subject matches the Subject Regex **and** the email body matches the Content Regex.

**Required Fields:**

- **Type**: `onInteractionEmailSent`
- **Subject Regex**: Regular expression pattern to match email subject (max 500 characters)
- **Content Regex**: Regular expression pattern to match email body (max 500 characters)
- **Workflow ID** (or **Default**: true): Either specify a workflow or make it workflow-independent
- **Custom Code**: JavaScript code to execute when triggered

**Important Notes:**

- The listener runs for **every** sent email that matches, not only once per ticket
- An empty Subject Regex or Content Regex matches every email. An invalid pattern is compared as plain text
- Regex patterns are case-sensitive unless specified otherwise in the pattern
- The custom code runs as the user who sent the email. `getEmailData()` returns `{ from, subject, body }` of the sent email; `getRegexMatches()` returns the capture groups of the regex matches
- If the custom code fails, the email is still sent; the error is written to the server log

**Use Cases:**

- Track specific keywords in outgoing emails
- Log emails containing certain phrases
- Trigger actions based on email content (e.g., escalate tickets with "urgent" in subject)
- Create audit trail for compliance

**Regex Examples:**

**Match emails with "Invoice" in subject:**

```
Subject Regex: Invoice
Content Regex: (leave empty or .*)
```

**Match emails containing specific order numbers in body:**

```
Subject Regex: .*
Content Regex: ORDER-\d{6}
```

**Match urgent emails:**

```
Subject Regex: (?i)(urgent|critical|emergency)
Content Regex: .*
```

**Configuration Example:**

```
Type: onInteractionEmailSent
Subject Regex: (?i)urgent
Content Regex: .*
Custom Code:
  await setTicketValue("Priority", "High");
  await sendMailTo(["manager@example.com"], "Urgent email detected", "Priority Escalation");
```

#### 5. onTicketOverdue

**Description:** Triggers on a scheduled basis for open tickets whose monitored date field is older than a specified overdue threshold.

**When it triggers:** A background job checks all `onTicketOverdue` listeners at a fixed server interval (default: every 5 minutes, server setting `Worker:ListenerIntervalMinutes`). A ticket triggers the listener when its status is "Open" and the date in the **Checked Field** is older than the **Overdue** threshold.

**Required Fields:**

- **Type**: `onTicketOverdue`
- **Checked Field**: The date field to monitor (e.g., "Due Date")
- **Overdue**: Time threshold in milliseconds (e.g., 86400000 = 24 hours)
- **Custom Code**: JavaScript code to execute for each overdue ticket
- **Disabled**: Must be `false` for the listener to run

**Important Notes:**

- The **Interval** field is stored but **not used**. All overdue listeners are checked at the server interval above.
- Setting **Disabled** to `true` excludes the listener from the check
- Executes once for each ticket that meets the overdue criteria (see [once per ticket](#understanding-listener-types))
- Store dates in a consistent, unambiguous format (e.g., `yyyy-MM-dd`)

**Common Overdue Values:**

- 3600000 - 1 hour
- 86400000 - 24 hours (1 day)
- 604800000 - 7 days (1 week)

**Use Cases:**

- Send reminder notifications for overdue tickets
- Escalate tickets that haven't been updated
- Auto-update priority for old tickets
- Generate overdue reports

**Configuration Example:**

```
Type: onTicketOverdue
Checked Field: Due Date
Overdue: 172800000  (48 hours)
Disabled: false
Custom Code:
  await sendMailTo(
    [await getTicketValue("AssignedTo")],
    "Ticket #" + UniqueID + " is overdue!",
    "Overdue Ticket Reminder"
  );
```

## Special Configuration Fields

### Default Flag

**Type:** Boolean (checkbox)
**Default Value:** false

**Purpose:** The **Default** flag transforms workflow-dependent listeners into workflow-independent (global) listeners.

**How it works:**

- **Default = false**: Listener only triggers for tickets assigned to the specified Workflow ID
- **Default = true**: Listener triggers for **ALL** tickets regardless of workflow assignment, making it globally applicable

**When to use Default = true:**

- Organization-wide automation that applies to all tickets
- Global monitoring and notifications
- Universal field updates based on conditions
- System-wide audit logging

**Examples:**

**Workflow-Specific Listener (Default = false):**

```
Type: onValueChanged
Checked Field: Status
Checked Value: Closed
Default: false
Workflow ID: 5
Custom Code: await sendMailTo(["team@example.com"], "Project X ticket closed", "Notification")
```

→ Only triggers for tickets with Workflow ID = 5

**Global Listener (Default = true):**

```
Type: onValueChanged
Checked Field: Status
Checked Value: Closed
Default: true
Workflow ID: (can be null)
Custom Code: await setTicketValue("ClosedDate", Date())
```

→ Triggers for **all tickets** when Status changes to "Closed", regardless of workflow

### Disabled Flag

**Type:** Boolean (checkbox)
**Default Value:** false

**Purpose:** The **Disabled** flag completely stops listener execution without deleting the configuration.

**How it works:**

- **Disabled = false**: Listener is active and will trigger when conditions are met
- **Disabled = true**: Listener is completely inactive and will **never** trigger, even if conditions are met

**Special behavior for onTicketOverdue:**

- Setting **Disabled = true** excludes the listener from the scheduled overdue check
- Setting **Disabled = false** includes it again at the next check

**When to use Disabled = true:**

- Temporarily pause automation during maintenance
- Disable problematic listeners while debugging
- Seasonal or conditional listeners (enable/disable as needed)
- Testing alternative implementations

**Best Practice:** Use Disabled instead of deleting listeners to preserve configuration for future use.

## Listeners Table

![The listeners table](media/admin_guide_listeners_table.gif)

The Listeners Table:

- Displays all configured event listeners in a tabular format
- Shows Type, Checked Field, Checked Value, Workflow, Default, Disabled, and other type-specific columns
- Provides an interface to add, edit, and delete listeners
- Integrates with punch workflows and punch details

## Managing Event Listeners

### Add a Listener

1. Click the **Add Row (+)** button above the table

2. **Select the Type** from the dropdown (required first step)

3. Enter the **type-specific required fields:**
   - For **onWorkflowStepAccepted**: Checked Field (step number), Checked Value ("Accepted"), Workflow ID
   - For **onValueChanged**: Checked Field (field name), Checked Value (trigger value), Workflow ID or Default
   - For **onTicketCreated**: Default (typically true)
   - For **onInteractionEmailSent**: Subject Regex and/or Content Regex
   - For **onTicketOverdue**: Checked Field (date field), Overdue (milliseconds)

4. **Configure special flags:**
   - **Default**: Check to make listener workflow-independent (applies globally)
   - **Disabled**: Check to temporarily disable the listener

5. **Write Custom Code:**
   - Click the Custom Code cell to open the code editor
   - Write JavaScript using available [Custom Code](#custom-code) functions
   - Test and save the code

6. Press **Enter** or click the **Save icon** at the end of the row to save the new listener

### Edit a Listener

1. Click the **Edit (pencil) icon** on the listener row

2. Modify any fields:
   - Change Type (note: required fields will change)
   - Update Checked Field/Value
   - Modify regex patterns for email listeners
   - Adjust Checked Field/Overdue for overdue listeners
   - Toggle Default or Disabled flags
   - Update Custom Code

3. Click **Save** to apply changes

### Delete a Listener

1. Click the **Trash icon** on the listener row

2. Confirm deletion when prompted

**Note:** A deleted onTicketOverdue listener is no longer included in the scheduled overdue check.

### Test a Listener

**Before Activating:**

1. Configure the listener with Disabled = true
2. Test the custom code using the Custom Code editor
3. Verify the trigger conditions are correct
4. Set Disabled = false to activate

**Live Testing:**

1. Create or modify a ticket to match the trigger conditions
2. Verify the listener executes (check logs, email notifications, field updates)
3. Review any errors in the system logs

## Custom Code Integration

Event listeners utilize the full [Custom Code](#custom-code) API. When a listener triggers, the custom code executes with access to:

- All ticket fields via `getTicketValue()` and `setTicketValue()`
- Workflow control functions like `stepForwardJumpTo()`
- Communication functions like `sendMailTo()`
- File operations like `createPdfReport()`
- Utility functions like `Date()`

**Workflow jumps in listeners:** `stepForwardJumpTo()` and `stepBackwardJumpTo()` move the ticket's pending workflow step:

- A listener with a **Workflow ID** moves the pending step of that workflow.
- A **Default** listener moves the pending step only when the ticket has exactly one pending step. Otherwise nothing moves.
- A jump to the pending step's own number keeps the step pending. If the custom code fails, nothing moves.
- `onTicketOverdue` and `onEmailReceived` listeners cannot jump: the request is only written to the server log.

**Important:** Event listeners execute **after** the triggering event has already occurred and been saved to the database. They cannot prevent or block the event (unlike [Field Validators](#field-validators)).

## Comparison: Event Listeners vs Field Validators

| Feature                 | Event Listeners                             | Field Validators                 |
| ----------------------- | ------------------------------------------- | -------------------------------- |
| **Trigger Timing**      | After event occurs (post-save)              | Before save (immediate)          |
| **Blocking**            | Non-blocking (cannot prevent events)        | Blocks invalid changes           |
| **Purpose**             | React to changes, automate actions          | Prevent invalid data entry       |
| **User Feedback**       | Background execution, no immediate feedback | Immediate error message in toast |
| **Multi-field Support** | Single field per listener                   | Multiple fields per validator    |
| **Execution Context**   | Full Custom Code API                        | Validation-specific helpers      |

**When to use Event Listeners:**

- Send notifications when conditions are met
- Auto-populate fields after changes
- Trigger external integrations
- Log events for audit trail
- Schedule periodic checks

**When to use Field Validators:**

- Enforce data format requirements
- Validate business rules before saving
- Prevent invalid field values
- Cross-field validation
- Role-based field restrictions

See [Field Validators](#field-validators) for detailed validation documentation.

## Best Practices

### Listener Design

- Use descriptive naming in Custom Code comments
- Test listeners thoroughly before deploying to production
- Use Default = true sparingly (only for truly global automation)
- Keep custom code focused and maintainable

### Performance

- Avoid heavy processing in frequently-triggered listeners
- For onTicketOverdue, choose an Overdue threshold that matches the business need; the check runs at a fixed server interval
- Consider using Disabled flag during high-load periods

### Error Handling

- Include error handling in custom code (try/catch blocks)
- Test edge cases (null values, missing fields, etc.)
- Monitor system logs for listener execution errors

### Regex for Email Listeners

- Test regex patterns before deploying
- Use case-insensitive matching when appropriate: `(?i)pattern`
- Escape special regex characters: `\.`, `\?`, `\+`, etc.
- Keep patterns under 500 characters

### Workflow Integration

- Document which workflows have listeners attached
- Coordinate with workflow designers to avoid conflicts
- Use Default flag consistently across related listeners

# Custom Code

The **Custom Code** feature enables administrators to write JavaScript code that executes custom logic and automation throughout the Toolkit application. This powerful feature is used in multiple components:

1. **Workflow Steps** - Execute actions when workflow steps are accepted or disapproved
2. **Email Message Templates** - Generate dynamic email content with ticket data
3. **Punchfield Mapping** - Transform attribute values during ticket creation
4. **Event Listeners** - Automate actions triggered by specific events (see [Event Listeners](#event-listeners))
5. **Field Validators** - Enforce custom validation rules on ticket fields (see [Field Validators](#field-validators))

## Security & Execution Environment

Custom code is run as JavaScript in a **secured sandbox environment** with the following protections:

- **Sandboxed Execution**: Restricts access to internal system functions and prevents malware execution
- **Code Length Limit**: Maximum 50,000 characters per code block. Enforced when the code is saved (workflow steps, event listeners, email message templates and field validators) and when it is tested with the Compile button
- **Dangerous Pattern Detection** (field validators only): When a field validator is saved, code containing SQL statements, `eval()`, `require()`, process access, prototype access and other risky patterns is rejected. Workflow step code and event listener code are not checked for these patterns.
- **Server-Side Execution**: All custom code runs on the server, never exposing sensitive operations to the client

## Available Objects

Custom code has access to the following predefined objects:

- **`ActionResult`**: Enum containing workflow action results

  ```javascript
  ActionResult = {
    Yes: "Yes",
    No: "No",
    Undefined: "Undefined",
  };
  ```

- **`userAction.Result`**: Contains the result of the current workflow step action (ActionResult value)

- **`language`**: String containing the current user's language code (e.g., "en-US", "ko")

**Usage example:**

```javascript
if (userAction.Result === ActionResult.Yes) {
  console.log("The step was approved");
  await setStatus("Approved");
} else {
  console.log("The step was disapproved");
  await setStatus("Rejected");
}
```

## Available Functions

### Ticket Management Functions

- **`getPunchlistDetails()`**
  Returns an **array** with one row per ticket field value: `{ PunchlistDetails: { Detail }, Value, PunchDetailID }`. It does not return a name→value object. For a single field, use `getTicketValue()`.

  ```javascript
  const rows = await getPunchlistDetails();
  const stage = rows.find((r) => r.PunchlistDetails.Detail === "Stage")?.Value;
  console.log(stage); // "In Review"
  ```

- **`updatePunchlistDetails(data: Map<string, string>[])`**
  Updates multiple ticket fields with new values. Takes an array of objects with `Detail` (field name) and `Value` properties.

  ```javascript
  await updatePunchlistDetails([
    { Detail: "Stage", Value: "In Progress" },
    { Detail: "Assigned To", Value: "John Doe" },
  ]);
  ```

- **`getTicketValue(fieldName: string)`**
  Returns the current value of a specific ticket field, or `null` when the ticket has no value for it.

  ```javascript
  const priority = await getTicketValue("Priority");
  ```

  > **Note:** `getTicketValue` reads only the ticket fields configured under [Ticket Fields](#ticket-fields). Built-in ticket properties such as `PunchItem` and `Status` are not ticket fields: `getTicketValue("PunchItem")` and `getTicketValue("Status")` return `null`. Use the `UniqueID` variable for the ticket number.

- **`setTicketValue(fieldName: string, value: any)`**
  Sets the value of a specific ticket field.

  ```javascript
  await setTicketValue("Priority", "High");
  await setTicketValue("Due Date", Date("yyyy-MM-dd"));
  ```

- **`createHiddenField(detail: any)`**
  Creates a field that is specific only to the current ticket item (not visible in field configuration).

  ```javascript
  await createHiddenField({ Detail: "InternalNotes", Value: "Auto-generated" });
  ```

- **`setStatus(status: string)`**
  Sets the status of the current ticket to the specified value.

  ```javascript
  await setStatus("Closed");
  ```

- **`closeWorkflow()`**
  Closes the current workflow and sets the ticket status to "Closed".

  ```javascript
  if (userAction.Result === ActionResult.Yes) {
    await closeWorkflow();
  }
  ```

### Workflow Control Functions

- **`stepForwardJumpTo(stepNumber: number, message?: string)`**
  Jumps to the specified workflow step number as a success/forward action. If a message is provided, it is printed to the activity chat. **This function overrides the Forward Step ID column value.**

  ```javascript
  // Jump to step 5 with a message
  await stepForwardJumpTo(5, "Skipping steps 2-4 - auto-approved");
  ```

  > **Note:** This function has priority over the default Forward Step ID column when utilized for [workflows](#4-workflow-columns).

- **`stepBackwardJumpTo(stepNumber: number, message?: string)`**
  Jumps to the specified workflow step number as a failure/backward action. If a message is provided, it is printed to the activity chat. **This function overrides the Backward Step ID column value.**

  ```javascript
  // Jump back to step 1 with a message
  await stepBackwardJumpTo(1, "Additional information required");
  ```

  > **Note:** This function has priority over the default Backward Step ID column when utilized for [workflows](#4-workflow-columns).

  > **Note:** Jumping to the step's **own** number (with either function) keeps the step pending. Nothing is recorded and the user sees no "Step approved" message — use `showPopup` to explain.

  ```javascript
  // Code on step 1: wait until the CTR discipline is filled in
  if (!(await getTicketValue("CTR Discipline First Action Owner"))) {
    await showPopup("Please fill in the CTR discipline before approving.");
    await stepBackwardJumpTo(1); // stay on step 1
    return false;
  }
  await stepForwardJumpTo(2);
  ```

### Communication Functions

- **`showPopup(message: string)`**
  Shows a popup with the message to the user whose action ran the code, after the step has been processed (Approvals tab and Kanban board). For an automatic `None` step that is the user who attached the workflow or who accepted/disapproved the step before it. Several calls, including those of chained `None` steps, are shown as one popup with one line per call. Not shown for event listeners or when no user is involved (written to the server log instead); Compile prints `showPopup: <message>`.

  ```javascript
  await showPopup("The ticket was forwarded to the modelling discipline.");
  ```

- **`sendMailTo(recipients: string[], message: string, subject: string, attachments?: any[])`**
  Sends an email to the specified recipients with optional attachments.

  ```javascript
  await sendMailTo(
    ["user@example.com", "manager@example.com"],
    "Ticket #" + UniqueID + " has been approved.",
    "Ticket Approval Notification",
  );

  // With attachments
  const report = await createPdfReport();
  await sendMailTo(
    ["user@example.com"],
    "Please see attached report.",
    "Weekly Report",
    [report],
  );
  ```

  Only objects returned by `createPdfReport()` and `extractUpvf()` can be attached. Other objects in the attachments array are ignored.

- **`createComment(message: string)`**
  Creates a comment on the current ticket.

  ```javascript
  await createComment("Automatically updated by workflow step 3");
  ```

### File Operations Functions

- **`createPdfReport(templateName?: string)`**
  Renders the stored ticket report template (see [Ticket Report Designer](#ticket-report-designer)) for the current ticket. It returns an attachment handle `{ filename, contentType }` for `sendMailTo`, not the PDF bytes. The `templateName` argument is ignored, because only one report template is stored. Other arguments are also ignored. On a rendering error the function returns `null`.

  ```javascript
  const report = await createPdfReport();
  await sendMailTo(["user@example.com"], "Report attached", "Ticket Report", [report]);
  ```

- **`extractUpvf(fileName?: string)`**
  Returns an object `{ filename, content, contentType, fileInfo }` for a UPVF file of the ticket, or `null` when the ticket has no UPVF file. `content` holds the file bytes as a `Uint8Array`. If `fileName` is given and a file name contains it, that file is used. Otherwise the first UPVF file of the ticket is used. The object can be attached with `sendMailTo`.

  ```javascript
  const upvf = await extractUpvf();
  if (upvf) console.log(upvf.filename, upvf.content.length, "bytes");
  // Or specific file
  const specificFile = await extractUpvf("project-model-v2.upvf");
  ```

- **`deleteUpvf(fileID: number)`**
  Hides a UPVF file by its ID (soft delete). Use `fileInfo.ID` from `extractUpvf()`.

  ```javascript
  await deleteUpvf(123);
  ```

### Utility Functions

- **`Date(formatString?: string)`**
  Returns the current date formatted according to the specified format string. Default format is "yyyy-MM-dd".

  ```javascript
  await setTicketValue("CreatedDate", Date()); // "2025-12-01"
  await setTicketValue("Timestamp", Date("yyyy-MM-dd HH:mm:ss")); // "2025-12-01 14:30:00"
  ```

## Practical Examples

### Example 1: Auto-populate fields based on a field change

```javascript
// When the "Stage" field is "Closed", auto-populate closure date and notify team
if ((await getTicketValue("Stage")) === "Closed") {
  await setTicketValue("ClosedDate", Date("yyyy-MM-dd"));
  await setTicketValue("ClosedBy", language === "en-US" ? "System" : "システム");

  await sendMailTo(
    ["team@example.com"],
    "Ticket #" + UniqueID + " has been closed.",
    "Ticket Closure Notification",
  );
}
```

### Example 2: Conditional workflow jumps

```javascript
// If priority is "Critical", skip review step and go directly to approval
const priority = await getTicketValue("Priority");
if (userAction.Result === ActionResult.Yes && priority === "Critical") {
  await stepForwardJumpTo(5, "Critical priority - skipping review step");
} else if (userAction.Result === ActionResult.Yes) {
  await stepForwardJumpTo(3, "Moving to standard review");
}
```

### Example 3: Cross-field updates

```javascript
// When Area is selected, auto-populate System based on mapping
const area = await getTicketValue("Area");
const areaSystemMap = {
  "Engine Room": "Propulsion",
  Bridge: "Navigation",
  "Cargo Hold": "Cargo Handling",
};

if (areaSystemMap[area]) {
  await setTicketValue("System", areaSystemMap[area]);
}
```

### Example 4: Date calculations and validations

```javascript
// Calculate and set due date to 7 days from now
const today = new Date();
const dueDate = new Date(today.setDate(today.getDate() + 7));
const formattedDueDate = dueDate.toISOString().split("T")[0];
await setTicketValue("DueDate", formattedDueDate);

// Notify if ticket is overdue
const currentDueDate = new Date(await getTicketValue("DueDate"));
if (currentDueDate < new Date() && (await getTicketValue("Stage")) !== "Closed") {
  await sendMailTo(
    [await getTicketValue("AssignedTo")],
    "Ticket #" + UniqueID + " is overdue!",
    "OVERDUE: Action Required",
  );
}
```

### Example 5: Multi-language support

```javascript
// Send notifications in user's language
const statusMessage =
  language === "ko" ? "티켓이 승인되었습니다" : "Ticket has been approved";

await createComment(statusMessage);
await sendMailTo(
  [await getTicketValue("AssignedTo")],
  statusMessage,
  language === "ko" ? "티켓 승인" : "Ticket Approval",
);
```

# Field Validators

The **Field Validators** section provides administrators with powerful tools to enforce custom business rules and data validation on ticket fields. Field validators trigger **immediately when ticket detail values change**, validating the new value **before** it is saved to the database.

Unlike [Event Listeners](#event-listeners) which **react** to changes after they occur, field validators **prevent invalid data** from being saved in the first place. When a validator fails, the user receives an immediate error message via toast notification, and the field retains its previous value.

Field validators use [Custom Code](#custom-code) with specialized validation functions to enforce rules such as:

- Data format requirements (uppercase, email format, regex patterns)
- Business logic validation (date ranges, conditional requirements)
- Cross-field dependencies (multi-field validation)
- Role-based field restrictions (admins only, etc.)

## Accessing Field Validators

To manage field validators:

1. Navigate to **Settings → Workflows → Validators** in the administration menu
2. Select the workflow you want to configure from the dropdown
3. Field validators are workflow-specific - they only apply when the selected workflow is active on a ticket

## Key Differences from Event Listeners

| Feature                 | Field Validators                                 | Event Listeners                                   |
| ----------------------- | ------------------------------------------------ | ------------------------------------------------- |
| **Trigger Timing**      | Before save (immediate, on field change)         | After save (event-driven)                         |
| **Blocking**            | **Blocks invalid changes** - prevents save       | Non-blocking - cannot prevent events              |
| **Purpose**             | **Prevent** invalid data from entering system    | **React** to data changes with automation         |
| **User Feedback**       | Immediate error message in toast notification    | Background execution, no immediate feedback       |
| **Multi-field Support** | Yes - one validator can validate multiple fields | Single field per listener                         |
| **Execution Context**   | Validation-specific helper functions             | Full Custom Code API                              |
| **Workflow Scope**      | Workflow-specific (only when workflow is active) | Can be workflow-specific or global (Default flag) |

**When to use Field Validators:**

- Enforce data format requirements (uppercase, email, phone numbers)
- Validate business rules before saving (end date > start date)
- Prevent invalid field values (negative numbers, invalid statuses)
- Cross-field validation (if A then B is required)
- Role-based field restrictions (only admins can set certain values)

**When to use Event Listeners:**

- Send notifications when conditions are met
- Auto-populate fields after changes
- Trigger external integrations or workflows
- Log events for audit trail
- Schedule periodic checks

## Validator Configuration

### Validator Fields

When creating or editing a field validator, you configure the following:

- **Workflow** - Select which workflow this validator applies to (required - validators are workflow-scoped)
- **Fields** - Multi-select TagBox to choose one or more fields to validate
  - **Single-field validation**: Select one field (e.g., "Description")
  - **Cross-field validation**: Select multiple fields (e.g., "Start Date" and "End Date")
  - When multiple fields are selected, the validator runs whenever ANY of those fields change
- **Error Message** - The message shown to the user when validation fails (max 500 characters)
  - Be specific and actionable (e.g., "End Date must be after Start Date")
- **Active** - Toggle to enable/disable the validator without deleting it
- **Order** - Execution order (drag-and-drop to reorder)
  - Validators execute in ascending order
  - Lower numbers execute first
  - If any validator fails, subsequent validators still run (all errors are collected)
- **Custom Code** - JavaScript validation logic that returns `true` (allow) or `false` (block)

### Validator Execution Flow

When a user changes a ticket field value:

1. **Check for Active Workflows**: System checks if the ticket has any active (non-closed) workflows assigned
2. **Find Applicable Validators**: For each active workflow, system fetches all **Active** validators that include the changed field
3. **Execute in Order**: Validators execute in ascending Order
4. **Collect Errors**: If any validator returns `false`, its ErrorMessage is added to the error list
5. **Block or Allow**:
   - **All validators pass** (`return true`): Change is saved to database
   - **Any validator fails** (`return false`): Change is **BLOCKED**, error messages shown in toast, field retains old value

## Writing Validation Code

Field validator custom code must return a boolean value:

- **`return true`** → Validation **PASSES** (allow save)
- **`return false`** → Validation **FAILS** (block save, show ErrorMessage)

### Validation-Specific Helper Functions

Field validators have access to specialized functions for validation context:

#### `getCurrentFieldValue()`

**Returns:** The value being validated (the new value the user is trying to save)

**Usage:**

```javascript
// Ensure Description field is uppercase
return getCurrentFieldValue() === getCurrentFieldValue().toUpperCase();
```

#### `getFieldValue(fieldName: string)`

**Args:** `fieldName` - Name of any field in the ticket
**Returns:** Current value of the specified field

**Usage:**

```javascript
// Cross-field validation: End Date must be after Start Date
const startDate = new Date(getFieldValue("Start Date"));
const endDate = new Date(getCurrentFieldValue());
return endDate >= startDate;
```

#### `getAllFields()`

**Returns:** Object containing all field names and their current values

**Usage:**

```javascript
// Multi-field business logic
const fields = getAllFields();
if (fields["Status"] === "Closed" && !fields["Resolution"]) {
  return false; // Error: "Resolution required for closed tickets"
}
return true;
```

#### `hasRole(roleName: string)`

**Args:** `roleName` - Name of the role to check
**Returns:** `true` if current user has the specified role, `false` otherwise

**Usage:**

```javascript
// Only admins can set Priority to "Critical"
if (getCurrentFieldValue() === "Critical") {
  return hasRole("Admin");
}
return true;
```

### Available Context Variables

In addition to helper functions, the following variables are available directly in validation code:

- **`currentValue`** - The field value being validated (same as `getCurrentFieldValue()`)
- **`fieldName`** - Name of the field being validated (e.g., "Description", "Status")
- **`punchDetails`** - Object with ALL field values for the ticket (same as `getAllFields()`)
- **`userRoles`** - Array of current user's role names (e.g., `["Admin", "Editor"]`)

## Validation Examples

### Example 1: Format Validation - Uppercase Only

Ensure a field only contains uppercase letters:

```javascript
// Validation code for "Description" field
return getCurrentFieldValue() === getCurrentFieldValue().toUpperCase();
```

**Error Message:** "Description must be in uppercase letters only"

### Example 2: Format Validation - Email Format

Validate email address format:

```javascript
// Validation code for "Email" field
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(getCurrentFieldValue());
```

**Error Message:** "Please enter a valid email address"

### Example 3: Cross-Field Date Validation

Ensure End Date is after Start Date:

```javascript
// Validation code for "Start Date" and "End Date" fields (both selected)
if (fieldName === "End Date") {
  const startDate = new Date(getFieldValue("Start Date"));
  const endDate = new Date(getCurrentFieldValue());
  return endDate >= startDate;
}
return true; // For Start Date changes, allow (End Date validator will catch issues)
```

**Error Message:** "End Date must be on or after Start Date"

### Example 4: Role-Based Validation

Only admins can set priority to "Critical":

```javascript
// Validation code for "Priority" field
if (getCurrentFieldValue() === "Critical") {
  return hasRole("Admin");
}
return true; // Allow all other priority values
```

**Error Message:** "Only administrators can set Priority to Critical"

### Example 5: Conditional Required Field

Resolution is required when Status is "Closed":

```javascript
// Validation code for "Status" and "Resolution" fields (both selected)
const fields = getAllFields();

if (fieldName === "Status" && getCurrentFieldValue() === "Closed") {
  // When closing, Resolution must be filled
  return fields["Resolution"] && fields["Resolution"].trim() !== "";
}

if (fieldName === "Resolution" && fields["Status"] === "Closed") {
  // When status is already closed, Resolution cannot be empty
  return getCurrentFieldValue() && getCurrentFieldValue().trim() !== "";
}

return true;
```

**Error Message:** "Resolution is required when Status is Closed"

### Example 6: Numeric Range Validation

Ensure Budget is within acceptable range:

```javascript
// Validation code for "Budget" field
const budget = parseFloat(getCurrentFieldValue());

// Check for valid number
if (isNaN(budget)) {
  return false;
}

// Check range
return budget >= 0 && budget <= 1000000;
```

**Error Message:** "Budget must be between 0 and 1,000,000"

### Example 7: Complex Multi-Field Business Logic

Validate project milestone dates and dependencies:

```javascript
// Validation code for "Phase Start", "Phase End", "Project Status" fields
const fields = getAllFields();
const phaseStart = new Date(getFieldValue("Phase Start"));
const phaseEnd = new Date(getFieldValue("Phase End"));
const projectStatus = getFieldValue("Project Status");

// Validate date range
if (fieldName === "Phase End" || fieldName === "Phase Start") {
  if (phaseEnd <= phaseStart) {
    return false; // End must be after start
  }
}

// Cannot set status to "In Progress" without phase dates
if (
  fieldName === "Project Status" &&
  getCurrentFieldValue() === "In Progress"
) {
  if (!fields["Phase Start"] || !fields["Phase End"]) {
    return false;
  }
}

return true;
```

**Error Message:** "Phase End must be after Phase Start, and phase dates are required before starting the project"

## Management Actions

### Add a Validator

1. Navigate to **Settings → Workflows → Validators**
2. Select the target workflow from the dropdown
3. Click the **Add** button (plus icon)
4. **Configure the validator**:
   - **Fields**: Click the TagBox and select one or more fields to validate
   - **Error Message**: Enter a clear, specific error message
   - **Active**: Check to enable (default: true)
   - **Order**: Set execution order (default: 0)
   - **Custom Code**: Click to open the integrated code editor
5. **Write validation code**:
   - Write JavaScript code that returns `true` (pass) or `false` (fail)
   - Use helper functions: `getCurrentFieldValue()`, `getFieldValue()`, `getAllFields()`, `hasRole()`
6. **Test the code**:
   - Click **Compile** button to check syntax errors
   - Click **Test** button to run with sample data
   - Review any error messages
7. Click **Save** in the code editor
8. Click **Save** in the validator form to persist to database

### Edit a Validator

1. Locate the validator in the table
2. Double-click the row OR click the **Edit (pencil)** icon
3. Modify any fields:
   - Add/remove fields from the TagBox
   - Update error message
   - Toggle Active status
   - Change Order
   - Update Custom Code
4. Re-test if code was changed (Compile/Test buttons)
5. Click **Save** to apply changes

### Delete a Validator

1. Locate the validator in the table
2. Click the **Trash** icon at the end of the row
3. Confirm deletion in the dialog

**Warning:** Deleted validators cannot be recovered. Consider using **Active = false** instead to preserve configuration.

### Reorder Validators

Execution order matters - validators execute in ascending Order:

1. Locate the **six-dot handle** (⋮⋮) at the left of each validator row
2. Click and drag the handle up or down
3. Release to drop the validator in the new position
4. The Order field updates automatically
5. Validators now execute in the new order

**Best Practice:** Order validators from simple to complex:

- Simple format checks first (fail fast)
- Cross-field validations next
- Complex business logic last

## Testing Validators

### Using the Integrated Code Editor

Before activating a validator, test it thoroughly:

1. **Syntax Check**:
   - Click the **Compile** button
   - Review any syntax errors highlighted in red
   - Fix errors and compile again

2. **Test with Sample Data**:
   - Click the **Test** button
   - The editor runs your code with sample field values
   - Review the console output
   - Verify return value is boolean (`true` or `false`)

3. **Review Error Messages**:
   - Ensure your Error Message is clear and actionable
   - Test with invalid data to see the message

### Live Testing

After saving a validator, test it with real tickets:

1. **Create or open a test ticket** that has the configured workflow assigned
2. **Try to enter an invalid value** in the validated field
3. **Verify the behavior**:
   - Error message appears in toast notification at top of screen
   - Toast is red/error styled
   - Field retains its old value (change is blocked)
   - You cannot save the invalid value

4. **Try entering a valid value**:
   - No error message appears
   - Field updates successfully
   - Value is saved to database

5. **Test edge cases**:
   - Empty values
   - Null values
   - Boundary values (min/max)
   - Special characters
   - Very long strings

### Debugging Validators

If a validator isn't working as expected:

1. **Check the basics**:
   - Validator is **Active** (checkbox checked)
   - Workflow is assigned to the ticket
   - Field name matches exactly (case-sensitive)
   - Field is included in the validator's Fields list

2. **Review execution order**:
   - Earlier validators might be blocking before yours runs
   - Check Order values and execution sequence

3. **Test the code in isolation**:
   - Use Compile button to check syntax
   - Use Test button with sample data
   - Add `console.log()` statements for debugging
   - Check browser console for runtime errors

4. **Verify field names**:
   - Field names in `getFieldValue('FieldName')` must match exactly
   - Check field configuration in **Punchlist Fields** section
   - Names are case-sensitive

5. **Test with simple code first**:

```javascript
// Simplest test - always fail
return false;
```

If this doesn't show an error, there's a configuration issue (not a code issue)

## Best Practices

### Error Messages

- **Be specific**: "End Date must be after Start Date" not "Invalid date"
- **Be actionable**: Tell users what they need to fix
- **Use examples**: "Format: ABC-12345" or "Must be uppercase"
- **Keep it concise**: Under 200 characters when possible
- **Avoid technical jargon**: Write for end users, not developers

### Validation Code

- **Keep it simple**: Complex logic is harder to maintain and debug
- **Fail fast**: Check simple conditions first (null, empty, format)
- **Handle edge cases**: Check for null, undefined, empty strings
- **Use consistent return values**: Always return boolean, never undefined
- **Comment your code**: Explain business rules for future maintainers
- **Test thoroughly**: Test with valid, invalid, null, and boundary values

### Multi-Field Validators

- **Be explicit**: Use `fieldName` to check which field triggered the validation
- **Validate both directions**: If validating A against B, handle changes to both A and B
- **Consider order**: Validators run for all included fields

### Performance

- **Avoid heavy operations**: Validators run on every field change
- **Don't call external APIs**: Validators must be fast and synchronous
- **Minimize getAllFields() calls**: Call once and store result if needed multiple times
- **Consider validator count**: Too many validators can slow down field updates

### Organization

- **Use descriptive Order values**: 10, 20, 30 instead of 1, 2, 3 (easier to insert new ones)
- **Group related validators**: Use Order to keep related validations together
- **Document complex logic**: Add comments explaining business rules
- **Review regularly**: Outdated validators should be deactivated or deleted

### Security

- **Never trust client data**: Always validate on the server (validators run server-side ✓)
- **Use role-based validation**: Restrict sensitive fields with `hasRole()`
- **Validate format strictly**: Use regex for formats like email, phone, IDs
- **Prevent injection**: Don't use `eval()` or construct SQL queries in validators

## Troubleshooting

### Validator Not Triggering

**Problem:** Validator doesn't execute when field changes

**Solutions:**

1. Verify validator is **Active** (checkbox checked)
2. Check that the workflow is assigned to the ticket
3. Confirm workflow is not **Closed** (validators only run on active workflows)
4. Verify field name matches exactly (case-sensitive)
5. Check that field is in the validator's Fields list
6. Review Order - try setting to 0 to execute first

### Custom Code Errors

**Problem:** Validator code throws runtime errors

**Solutions:**

1. Use **Compile** button to check syntax errors
2. **Test** with sample data before saving
3. Check browser console for detailed error messages
4. Verify field names in `getFieldValue()` calls are correct
5. Handle null/undefined values:

```javascript
const value = getCurrentFieldValue();
if (!value) return false; // Require non-empty
```

6. Ensure you always return a boolean (`true` or `false`)

### Validation Too Strict

**Problem:** Validator blocks valid data

**Solutions:**

1. Review validation logic - may be too restrictive
2. Test edge cases (empty, null, special characters)
3. Consider role-based exceptions using `hasRole()`
4. Update error message to clarify requirements
5. Add logging to understand what values are being rejected:

```javascript
console.log("Validating:", getCurrentFieldValue());
```

### Validation Too Loose

**Problem:** Validator allows invalid data

**Solutions:**

1. Review validation logic - may have loopholes
2. Test with known invalid values
3. Check for missing null/empty checks
4. Ensure you're returning `false` for invalid cases
5. Verify regex patterns match your requirements

### Cross-Field Validation Issues

**Problem:** Multi-field validation not working correctly

**Solutions:**

1. Check which field triggered with `fieldName` variable
2. Ensure both fields are included in the Fields list
3. Handle validation for each field explicitly:

```javascript
if (fieldName === "StartDate") {
  // Validate start date logic
}
if (fieldName === "EndDate") {
  // Validate end date logic
}
```

4. Use `getFieldValue()` to get the other field's value
5. Consider creating separate validators for each field

### Performance Issues

**Problem:** Field updates are slow when validators are active

**Solutions:**

1. Reduce number of validators
2. Simplify complex validation logic
3. Remove unnecessary `getAllFields()` calls
4. Avoid loops and recursion in validation code
5. Consider combining multiple simple validators into one
6. Use **Disabled** flag to temporarily pause validators during bulk updates

## API Reference

For detailed information about all available Custom Code functions, see the [Custom Code](#custom-code) section.

**Validation-specific functions:**

- `getCurrentFieldValue()` - Get value being validated
- `getFieldValue(fieldName)` - Get any field value
- `getAllFields()` - Get all field values
- `hasRole(roleName)` - Check user role

**Available variables:**

- `currentValue` - Value being validated
- `fieldName` - Field being validated
- `punchDetails` - All field values
- `userRoles` - User's roles array

# Comment Configuration

The **Comment Configuration** section allows administrators to customize how comments are automatically generated when creating punch items using the "**Create with comment**" option. This feature enables a standardized naming convention for comments by selecting which punchlist detail fields should be included in the generated comment description.

![Comment Generation](media/admin_guide_comment_generation.gif)

## Overview

- Comment Configuration controls the automatic generation of comment descriptions when punch items are created with associated comments.

- Administrators can select up to **three** mapped punchlist detail fields to include in the comment description.

- The selected fields determine how the comment will be named, following the format: "PI-[PunchID] : [Field1Value] : [Field2Value] : [Field3Value]".

- Only punchlist details that have been mapped in the **Attribute Mapping** section are available for selection.

- The feature includes a toggle to enable or disable automatic comment generation entirely.

## Features

### 1. Mapped Details Selection

The page displays all punchlist details that have been mapped to 3D attributes, 2D attributes, or custom code in the Attribute Mapping section.

- Each mapped detail is shown with its name and mapping information (3D attribute, 2D attribute, or code mapping).

- If no mapped details are available, the page prompts administrators to configure attribute mappings first.

### 2. Field Selection for Comments

Administrators select which fields to include in auto-generated comments:

- Click the **Check** button next to a detail to add it to the comment configuration.

- Up to **3 fields** can be selected at once.

- If you select a 4th field while 3 are already selected, the first selected field is automatically removed.

- The order in which fields are selected determines their appearance order in the generated comment.

- Fields can be deselected by clicking their **Check** button again.

### 3. Comment Preview

- The **Ticket Comment Preview** section shows a live preview of how the selected fields will appear in the comment.

- Fields are displayed in the format: "Field1-Field2-Field3", separated by hyphens.

- A counter shows how many fields are currently selected (e.g., "Selected: 2/3").

- If no fields are selected, the preview displays "No fields selected".

### 4. Comment Generation Toggle

The **Comment Generation Enabled** checkbox controls whether automatic comment creation is active:

- When enabled, punch items created with the "Create with comment" option will automatically generate comments using the configured field values.

- When disabled, no automatic comments will be generated regardless of field selection.

- This toggle updates immediately without requiring a save action.

# Components

The **Components** section allows administrators to customize the available attributes and control default column visibility for the components table in the main punchlist details (Components tab).

![Components Page](media/admin_guide_components_page.png)

### Features

1. All available model attributes are displayed in the **Available Model Attributes** List.
   Click an attribute to move it from the **Available Model Attributes** list to the **Selected Attributes** list.
   Attributes in the **Selected Attributes** list are available as columns in the main components table.
2. To remove an attribute, click it in the **Selected Attributes** list to move it back to the **Available Model Attributes** list.

3. Four columns are permanent and cannot be removed: Type, UID, Bounding Box, and Drawing Name.

4. Administrators also have the ability to set the default visibility of each selected attribute using checkboxes located at the end of each row.

5. The visibility of the permanent columns can also be toggled as needed.

6. This configuration is applied by default for a user accessing the application for the first time.

![Components Page Actions](media/admin_guide_components_actions.gif)

# Accessible Pages

Navigate to **Enabled Pages** in the Admin menu to control which routes are visible in the application, configure the default landing page users are sent to after login, and manage the order and visibility of punchlist detail tabs.

![Enabled Pages](media/admin_guide_enabled_pages.png)

## Toolkit Pages

The **Toolkit Pages** table lists every registered application route.

### View and Edit Hidden Routes

1. Specify the route in the first column as `/<route>` — e.g. `/clashes`
2. Toggle the **Enabled** checkbox in the second column to allow or block access to that route

### Default Landing Page

Each enabled page can be designated as the **Default Landing Page** — the first screen users are redirected to immediately after a successful login.

- The **Default Landing Page** column shows a checkbox for each page row
- Only **enabled** pages can be set as the default; the checkbox is greyed out for disabled pages
- Only one page can be the default at a time — selecting a new page automatically clears the previous one
- **Unsetting the default is not allowed directly.** If you want to change the default, click the new page's checkbox; the old one clears automatically. You cannot uncheck the current default without first selecting a different one — an attempt to do so shows a warning toast

**How it affects login:**
After authentication, the application redirects the user to the configured default landing page. If no default is set, the first enabled non-settings page is used instead. This means you can point all users directly to the Tickets list, the LoToTo master list, or any other registered route without any code changes.

## Punchlist Tabs Visibility and Order

1. In the **Punchlist Tabs** table, administrators can toggle the **Visible** checkbox to control which tabs appear in the punchlist details section
2. The order of the tabs can be adjusted by dragging and dropping rows using the six-dot handle to the left of each row

# Ticket Report Designer

The **Ticket Report Designer** page provides a powerful, fully-featured PDF template editor that allows administrators to create and customize professional reports for ticket items. Built on the open source PDFME playground editor, this tool offers complete control over report layout, fields, and styling.

![Ticket Report Designer](media/admin_guide_ticketreport.png)

## Overview

- Design custom PDF reports with a visual drag-and-drop interface
- Map ticket detail fields to report elements
- Support for text, images, tables, signatures, QR codes, and more
- Real-time preview of report layouts
- Save templates to the database for use when generating ticket reports
- Load and modify existing templates or start from scratch

## Features

### 1. Visual Template Designer

The embedded PDFME designer provides an intuitive interface for creating PDF report templates:

- **Drag-and-Drop Elements**: Add text fields, images, shapes, tables, barcodes, QR codes, and signature fields to your template
- **Real-Time Preview**: See exactly how your report will look as you design it
- **Page Management**: Create multi-page reports with different layouts per page
- **Precise Positioning**: Use pixel-perfect positioning and alignment tools
- **Zoom Controls**: Adjustable zoom with a draggable zoom bar for detailed editing

### 2. Field Naming Convention

To populate report fields with actual ticket data, use the following naming conventions in the field "Name" property:

#### Regular Ticket Fields

**Format**: `reportData.DetailName`

**Example**: `reportData.Tag No`

- References the "Tag No" field from the Ticket Fields configuration
- Automatically populates with the value from the ticket's details
- Works with any custom field defined in the **Punchlist Fields** admin section

#### Screenshot Images

**Format**: `Initial` or `Final`

- `Initial` — Displays the uploaded initial screenshot image
- `Final` — Displays the uploaded final screenshot image
- These fields should be added as image elements in the designer

**Important**: The field name must exactly match the Detail name as defined in the Punchlist Fields administration page (case-sensitive).

### 3. Base PDF Management

**Change Base PDF**

- Upload a PDF file to use as the base template for your reports
- The base PDF appears as a background, with dynamic fields overlaid on top
- Useful for incorporating pre-designed features
- **File Requirements**: Must be a valid PDF file

**How to use**:

1. Click **"Change Base PDF (must be PDF)"** in the top navigation
2. Select a PDF file from your computer
3. The PDF pages become the background for your template
4. Add dynamic fields on top of the base PDF

### 4. Template Management

#### Save Template

- Click the **"Save"** button to save your current template design to the database
- The saved template is used when generating reports for tickets
- A success notification confirms when the template is saved
- Templates persist across sessions and are available to all users

#### Load Template

- Upload a previously saved template JSON file
- Useful for importing templates created elsewhere or restoring backups
- **File Requirements**: Must be a valid JSON file in PDFME format

**How to use**:

1. Click **"Load Template (must be JSON)"**
2. Select a JSON template file
3. The designer loads the template for editing

#### Download Template

- Export your current template as a JSON file
- Use this to create backups or share templates
- Click **"DL Template"** to download

#### Reset Template

- Click the **"Reset"** button to clear the current template
- Returns to a blank template with default settings
- **Warning**: This action cannot be undone

## Troubleshooting

**Fields not populating with data**

- Verify the field name matches exactly (case-sensitive) with the Detail name in Punchlist Fields
- Ensure the naming convention `reportData.DetailName` is used correctly
- Check that the ticket has data in the specified field

**Base PDF not displaying**

- Confirm the uploaded file is a valid PDF format
- Check the PDF file size is reasonable (under 10MB recommended)
- Verify the PDF is not password-protected or encrypted

**Template not saving**

- Check browser console for error messages
- Ensure you have administrator permissions
- Verify database connectivity

# Power BI

The **Power BI** section lets administrators manage embedded reports. Reports are grouped by type and configured in-app without modifying environment variables.

## 1.  One‑Time Azure & Power BI Setup

### Azure Setup for Power BI Embedding

Follow these 7 steps to configure your Azure and Power BI environment. Once complete, no additional Azure changes are required — everything else is managed inside the app.

| #   | Task                                   | What to Do                                                                                                                                                                                                                                     |
| --- | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1   | **Register an Azure AD App**           | Go to **Azure Portal** → **Azure Active Directory** → **App registrations** → **New registration**.<br>Save the **Application (Client) ID** and **Directory (Tenant) ID**.<br>_No extra API permissions are needed._                           |
| 2   | **Generate a Client Secret**           | Go to **Certificates & Secrets** → **New client secret**.<br>Copy the value and store it securely (used as `POWERBI_SECRET`).                                                                                                                  |
| 3   | **Enable Tenant Settings**             | In **Power BI Service** → **Admin Portal** → **Tenant Settings** ([direct link](https://app.powerbi.com/admin-portal/tenantSettings?experience=power-bi)), enable:<br>▪ Embed content in apps<br>▪ **Developer settings** → **Service principals can call Fabric public APIs** (formerly "Allow service principals to use Power BI APIs")<br>_Scope = Entire organization or a security group containing the service principal._ See the screenshot below. |
| 4   | **Add Service Principal to Workspace** | In each target workspace → **Access** → **Add member** → search for the app name.<br>Set **Role** to `Member` or `Admin`.                                                                                                                      |
| 5   | **Publish Reports**                    | Open **Power BI Desktop** and publish your `.pbix` file to the same workspace.                                                                                                                                                                 |
| 6   | **Provision Embedded Capacity**        | Go to **Azure Portal** → **Power BI Embedded** → choose a capacity tier (e.g., A1).<br>In Power BI workspace: **Settings** → **Premium** → assign the capacity.                                                                                |
| 7   | **(If report connects to Azure SQL)**  | In **SQL Server** → **Networking** → enable “Allow Azure services and resources to access this server”.                                                                                                                                        |

**Step 3 — Enable service principals in the Power BI tenant settings:**

![Power BI tenant setting: Service principals can call Fabric public APIs](media/admin_guide_powerbi_setup.png)

---

Once these steps are completed, your Azure environment is ready. All further actions are handled within the app.

## 2.  Server‑Side Environment Variables

- Only three secrets live in the deployment environment (never in the UI):

```json
POWERBI_TENANT_ID = <Directory / Tenant GUID>
POWERBI_CLIENT_ID = <Application / Client GUID>
POWERBI_SECRET = <Client Secret>
```

- The application reads them through:

```json
{
  authenticationMode: 'ServicePrincipal',
  authorityUrl: 'https://login.microsoftonline.com/',
  scopeBase: 'https://analysis.windows.net/powerbi/api/.default',
  tenantId:   process.env.POWERBI_TENANT_ID,
  clientId:   process.env.POWERBI_CLIENT_ID,
  clientSecret: process.env.POWERBI_SECRET,
}
```

- **Note**: Authentication Mode must remain ServicePrincipal – this is the Microsoft‑recommended approach for App‑Owns‑Data embeds and avoids user consent pop‑ups.

## 3. Adding & Maintaining Reports inside the App

Use the Power BI UI page to:

## Report Management Actions

| **Action**                 | **Steps**                                                                                                                                                                                                                                                     |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Add a Report**           | 1. Navigate to **Admin › Power BI**.<br>2. Click **“+ Add Report”**.<br>3. Enter: **Report Name**, **Report ID**, **Workspace ID**, and **Type** (copy GUIDs from Power BI Service URL).<br>4. Click **Save** – the app validates IDs by contacting Power BI. |
| **Edit a Report**          | Click **✎ (edit)** → change values → **Save**.                                                                                                                                                                                                                |
| **Delete a Report**        | Click **🗑** on the appropriate report to delete the report.                                                                                                                                                                                                  |
| **Create a Report Type**   | Type a new value into the Report Type field when adding a report — it will be created automatically.                                                                                                                                                          |
| **Delete a Report Type**   | Click **🗑** next to a type to remove it and all its reports.\_                                                                                                                                                                                               |
| **Hide / Show the Navbar** | Click **«** or **»** to collapse the left pane and view the report full‑width.                                                                                                                                                                                |

![Power BI Page](media/admin_guide_powerbi.gif)

# LOTO Administration

The **LOTO (Lockout/Tagout)** administration section enables administrators to configure templates, metadata fields, and catalogue items that define how Lockout/Tagout work programs operate throughout the application.

## Overview

The LOTO system provides a comprehensive framework for managing Lockout/Tagout work programs in industrial environments. Administrators configure:

- **Templates**: Reusable step sequences for different work program types
- **Metadata Fields**: Dynamic columns that appear in work program list tables and steps tables
- **Catalogue Items**: Placeable symbols and equipment types for P&ID drawings

Users leverage these configurations to create, execute, and track work programs with proper safety procedures and documentation.

## LOTO Templates

Navigate to **Settings → LOTO Settings** to manage reusable templates that define step sequences for work programs.

### What are LOTO Templates?

Templates are pre-configured sets of steps organized by phase (Preparation, De-commissioning, Repair, Commissioning) that can be applied to new work programs. They ensure consistency and best practices across similar work activities.

### Template Management Actions

#### **View All Templates**

The Templates page displays all available templates in a table format with the following columns:

- **Template Name**: Descriptive name for the template
- **Type**: Category or classification (e.g., "Maintenance", "Inspection")
- **Description**: Detailed explanation of the template's purpose
- **Category**: Grouping classification for organizing templates
- **Last Modified**: Date of last modification
- **Last Modified By**: User who last updated the template

#### Add a New Template

1. Click the **Add (+)** button in the toolbar
2. Enter the required fields:
   - **Template Name**: Unique identifier (required)
   - **Type**: Classification category
   - **Description**: Purpose and usage details
   - **Category**: Organizational grouping
3. Click **Save** to create the template
4. The template appears in the list and can now be opened to add steps

#### Edit Template Properties

1. Click the **Edit (pencil)** icon on the template row
2. Modify any field:
   - Template Name
   - Type
   - Description
   - Category
3. Click **Save** to apply changes

#### Delete a Template

1. Click the **Trash** icon on the template row
2. Confirm deletion in the dialog
3. **Warning**: Deleting a template does not affect work programs that were created using it. However, the template will no longer be available for new work programs.

#### Configure Template Steps

1. Double-click a template row OR click the template name
2. Navigate to the Template Steps page for that template

### Template Steps Configuration

After selecting a template, administrators can configure the steps for each phase.

#### Phase Tabs

Template steps are organized into four phases:

- **Preparation**: Initial setup and safety procedures
- **De-commissioning**: Shutdown and isolation procedures
- **Repair**: Maintenance or repair work activities
- **Commissioning**: Startup and restoration procedures

Click each tab to view and edit steps for that phase.

#### Steps Table Columns

| Column              | Description                                                                                                                                                                                                                          |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Order**           | Sequential number determining execution order (drag to reorder)                                                                                                                                                                      |
| **Step Title**      | Name of the step (required)                                                                                                                                                                                                          |
| **Step Type**       | Classification (e.g., "Safety", "Isolation", "Verification")                                                                                                                                                                         |
| **Dynamic Columns** | Additional columns defined via **LOTO Metadata Configuration** for the current phase (e.g., Responsible Party, Action Owner). These are configurable by administrators, and only fields with **Show in List** enabled are displayed. |
| **Comment**         | Additional notes, instructions, or requirements                                                                                                                                                                                      |
| **Interdependency** | Other steps (by Order number) that must be completed first                                                                                                                                                                           |

#### Add a Template Step

1. Select the phase tab (Preparation, De-commissioning, Repair, or Commissioning)
2. Click the **Add (+)** button
3. Enter step details:
   - **Step Title**: Clear, actionable description (required)
   - **Step Type**: Category or classification
   - **Dynamic fields**: Fill in any additional columns configured for this phase (e.g., Responsible Party, Action Owner). These are defined in **LOTO Metadata Configuration** with the corresponding phase as Item Type.
   - **Comment**: Detailed instructions or safety notes
   - **Interdependency**: Select prerequisite steps by Order number
4. Click **Save** or press Enter

#### Edit a Template Step

1. Click the **Edit (pencil)** icon on the step row
2. Modify any field
3. Click **Save** to apply changes

**Note**: Changes to template steps do NOT affect existing work programs that were created from this template. They only apply to newly created work programs.

#### Delete a Template Step

1. Click the **Trash** icon on the step row
2. Confirm deletion
3. The step is removed from the template

#### Reorder Template Steps

1. Locate the **six-dot handle** (⋮⋮) at the left of the row
2. Click and drag the step to the desired position
3. Release to drop
4. The Order numbers update automatically

#### Set Step Interdependencies

Interdependencies ensure that prerequisite steps must be completed before a step becomes available.

1. Edit the step
2. Click the **Interdependency** cell
3. Select one or more prerequisite steps by their Order number
4. Save the step

### Using Templates in Work Programs

When users create a new work program, they can import a template through the **Planning → Template** tab. The template steps are copied to the work program, including any dynamic field values defined on the template steps, where users can then execute them. Similarly, when cloning steps from an existing work program, dynamic field values are preserved in the copy.

**Key Point**: Template changes do not retroactively affect existing work programs. Only new work programs created after the template is modified will include the updates.

## LOTO Metadata Configuration

Navigate to **Settings → LOTO Metadata** to define the **dynamic columns (metadata fields)** that appear in work program tables. Fields can target either **list tables** (e.g., Blind List, Valves List, Electrical Drives List, Drains List, Hose List, Other List) or **steps tables** (Preparation, De-commissioning, Repair, Commissioning). These fields control what users can view and edit in each table.

### What you can do here

- **Add** new columns (fields) to specific lists.
- **Edit** field properties (labels, data source, input type, etc.).
- **Delete** fields you no longer want shown.
- **Reorder** fields by dragging rows to change the column order in the list tables.

## Field Reference

| Field                                | What it means / how it’s used                                                                                                                                                                                                                                           |
| ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Field Name**                       | Internal identifier; must be unique, start with a letter, and use only letters, numbers, or underscores.                                                                                                                                                                |
| **Display Label**                    | The column header shown to users in list tables.                                                                                                                                                                                                                        |
| **Item Type**                        | Determines which table shows this field. **List types**: Blind List, Valves List, Electrical Drives List, Drains List, Hose List, Isolation Markup. **Phase types**: Preparation, De-commissioning, Repair, Commissioning (columns appear in the Steps table for that phase). |
| **Show in List**                     | Toggle (default: **on**). When disabled, the field is hidden from list/steps table columns without deleting the field definition or its stored values. Re-enable to show the column again.                                                                              |
| **Include in Summary**               | Toggle (default: **on**). When enabled, this field is included in the exported summary PDF report tables.                                                                                                                                                               |
| **Column Width**                     | The pixel width applied to this column in list and steps tables. Defaults to **150**. Editable for all fields including hardcoded ones.                                                                                                                                  |
| **Data Source**                      | Where the value comes from: **UserEntered** (users type/select values), **ConnectedItem** (auto-filled from P&ID object), or **Hardcoded** (built-in system columns that cannot be created or removed by administrators). You can filter by this value using the column header filter to view only hardcoded rows. |
| **Connected Item Attribute**         | Attribute to display when **Data Source = ConnectedItem**.                                                                                                                                                                                                              |
| **Input Type (UserEntered only)**    | Editor control for manual entry: **Text**, **Dropdown**, **Boolean**, **Number**, or **Date**.                                                                                                                                                                          |
| **Dropdown Options (Dropdown only)** | Allowed choices for **Input Type = Dropdown**; provide as a comma-separated list.                                                                                                                                                                                       |
| **Data Type**                        | Must match the input type (**String/Number/Boolean/Date**); enforced automatically.                                                                                                                                                                                     |

## Common Operations

![Common Metadata Operations](media/admin_guide_metadata_operations.gif)

### Add a Field

1. Click the **"+"** button.
2. Fill out:
   - **Field Name:** Internal name (letters/numbers/underscores; must start with a letter).
   - **Display Label:** The column header users will see.
   - **Item Type:** Which table the field appears in. Choose a **list type** (e.g., Blind List) to add the column to that list table, or a **phase** (e.g., Preparation, De-commissioning) to add the column to the steps table for that phase.
   - **Data Source:** **UserEntered** (manually entered in lists) or **ConnectedItem** (auto from connected P&ID object).
   - **Input Type (UserEntered only):** **Text**, **Dropdown**, **Boolean**, **Number**, or **Date**.
   - **Dropdown Options (Dropdown only):** Comma-separated choices (e.g., `Open,Closed,Locked`).
   - **Data Type:**
     - If **UserEntered** → auto-matched and locked to the Input Type:  
       **Text → String**, **Dropdown → String**, **Boolean → Boolean**, **Number → Number**, **Date → Date**.
     - If **ConnectedItem** → choose **String/Number/Boolean/Date** to match the source attribute.
   - **Connected Item Attribute (ConnectedItem only):** Attribute to pull from the P&ID object.
3. Click the **Save Icon** button. The field is added and positioned at the end of the list’s columns.

### Edit a Field

1. Click the **Edit (pencil)** icon on the row.
2. Adjust the properties.
   - When **Data Source = ConnectedItem**, **Input Type** and **Dropdown Options** are disabled.
   - When **Data Source = UserEntered**, **Connected Item Attribute** is disabled.
3. Click the **Save Icon** button to finalize changes.

### Hide a Field (Show in List)

If you want to temporarily remove a column from the user-facing tables without deleting the field definition:

1. Edit the field row.
2. Uncheck the **Show in List** toggle.
3. Save. The column is immediately hidden from list/steps tables.
4. Re-enable the toggle at any time to restore the column. All stored values are preserved.

### Delete a Field

1. Click the **Delete (trash)** icon and confirm.
   - The column **disappears from the UI** for users.
   - Existing values in the database are **not deleted**.
   - Re-create the same **Field Name** later to show the values again.

### Reorder Fields (Column Order)

1. Drag the **row handle** up or down.
2. Release to drop. The new order is saved and reflected in the list table layout.

---

## Rules & Validation (Quick Guide)

- **Required fields:** Field Name, Display Label, Item Type, Data Source, Data Type.
- **Dropdown Options:** Required if **Input Type = Dropdown**.
- **Type Matching:** **Data Type** must match the **Input Type** (checked automatically).
- **Connected vs UserEntered:** Only the relevant inputs are enabled; the others are dimmed and cannot be edited.

## What End Users Will See

- Fields with a **list Item Type** (e.g., Blind List, Valves List) appear as **columns** in the corresponding work program list tables.
- Fields with a **phase Item Type** (e.g., Preparation, De-commissioning) appear as **columns** in the steps table and template steps table for that phase.
- Only fields with **Show in List** enabled are displayed as columns. Disabling this toggle hides the column from users without deleting the field or its data.
- **UserEntered** fields can be **edited inline** by users (double-click cell, edit, press **Enter** to save).
- **ConnectedItem** fields are **read-only**, reflecting live data from the connected P&ID object.
- **Hardcoded** fields (Data Source = Hardcoded) are built-in system columns seeded automatically per list type. Each list type (Blind List, Valves List, Electrical Drives List, Drains List, Hose List, Isolation Markup) has its own independent set of hardcoded entries. These rows are visible in the metadata mapping grid and their **Column Width** can be adjusted, but they cannot be created, deleted, or have their Data Source changed by administrators.
- **Type** is a special hardcoded column that automatically displays the **Connected Item Name** (the name of the catalogue item placed on the P&ID).

# Catalogue

Use this page to manage the **placeable symbols/templates** that appear in the Sketching/Placement tools for work program lists (e.g., **Blinds, Valves, Electrical Drives, Drains, Hoses, Other**). Each catalogue item corresponds to a symbol (SVG) that users can place onto P&IDs when creating or updating work program items.

### What is the LOTO Catalogue?

The LOTO Catalogue contains the library of symbols and equipment types that users can place on P&ID drawings when building work programs. Each catalogue item represents a physical device (blind, valve, electrical drive, etc.) and includes:

- A visual symbol (SVG file) for placement on drawings
- Associated actions for commissioning and de-commissioning (for Blinds, Valves, and Electrical Drives only)
- Classification by list type

### Catalogue Management Actions

- **Add**, **Edit**, **Delete**, and **Reorder** catalogue items
- **Effect for users:** Items you configure here appear in the **Add list item** flow and Sketching Tool catalogue lists

### Field Reference

| Field                   | Description                                                                                                                                                  |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Catalogue Item Name** | Friendly name shown to users in placement dialogs (e.g., "SC-1 Spade Blind", "Gate Valve 2-inch")                                                            |
| **SVG Path**            | Path to the SVG symbol file in the model/catalogue folder (e.g., `IntelliPidCatalogue\LOTO_Symbols\Lototo_V2_w_ll.svg`)                                      |
| **List Type**           | Which work program list this item appears under: **Blind List**, **Valves List**, **Electrical Drives List**, **Drains List**, **Hose List**, **Other List** |
| **Display Order**       | Hidden system field updated when you drag rows; controls visible order in placement pickers                                                                  |
| **Com Action**          | Commissioning action description (Blinds, Valves, Electrical Drives only)                                                                                    |
| **DeCom Action**        | De-commissioning action description (Blinds, Valves, Electrical Drives only)                                                                                 |
| **Com Action Owner**    | Responsible party for commissioning (Blinds, Valves, Electrical Drives only)                                                                                 |
| **DeCom Action Owner**  | Responsible party for de-commissioning (Blinds, Valves, Electrical Drives only)                                                                              |

**Note**: Action fields (Com Action, DeCom Action, Com Action Owner, DeCom Action Owner) are only available for **Blind List**, **Valves List**, and **Electrical Drives List**. These fields are automatically disabled for Drains List, Hose List, and Other List.

### Common Operations

![Common Catalogue Operations](media/admin_guide_catalogue_operations.gif)

#### Add a Catalogue Item

1. Click the **Add (+)** button
2. Fill out the required fields:
   - **Catalogue Item Name**: The name users see when selecting an item to place (e.g., "SC-1 Spade Blind", "Gate Valve 2-inch")
   - **SVG Path**: The file path to the symbol (SVG) in your model/catalogue folder (e.g., `IntelliPidCatalogue\LOTO_Symbols\Lototo_V2_w_ll.svg`)
     - Ensure the SVG exists and is accessible in the deployment environment
   - **List Type**: Which work program list this item belongs to
3. (Optional) For Blinds, Valves, and Electrical Drives, enter:
   - **Com Action**: Description of commissioning action
   - **DeCom Action**: Description of de-commissioning action
   - **Com Action Owner**: Responsible party for commissioning
   - **DeCom Action Owner**: Responsible party for de-commissioning
4. Click the **Save** icon button
5. The new item appears for users in the corresponding placement catalogue

**Tip**: Use descriptive names that help users quickly identify the correct item (e.g., "Spectacle Blind 6-inch" instead of just "Blind").

#### Edit a Catalogue Item

1. Click the **Edit (pencil)** icon on the row
2. Update any field:
   - Name
   - SVG Path
   - List Type
   - Action fields (if applicable)
3. Click **Save** to finalize changes

**Note**: If you change the List Type from Blinds/Valves/Electrical Drives to Drains/Hose/Other, the action fields are automatically cleared.

#### Delete a Catalogue Item

1. Click the **Delete (trash)** icon on the row
2. Confirm deletion in the dialog

**Important**:

- The item is **removed from the placement catalogue** for users
- **Already-placed objects** using that SVG are **not deleted** from P&IDs or work programs
- Users can no longer select this item for new placements

#### Reorder Catalogue Items

1. Drag the **six-dot handle** (⋮⋮) up or down to change order
2. Release to drop
3. The **Display Order** updates automatically
4. Items appear in this order in the placement picker for users

**Best Practice**: Order items logically (e.g., group similar items together, or order by frequency of use).

### SVG Symbol Requirements

- **File Format**: Must be a valid SVG file
- **Location**: Must exist in the model/catalogue folder accessible by the application
- **Path Format**: Use backslashes for Windows paths (e.g., `IntelliPidCatalogue\LOTO_Symbols\symbol.svg`)

# LoToTo Print Summary Templates

The **Print Summary Templates** page lets administrators upload and manage `.xlsx` Excel files that are used as the layout template when generating the LoToTo Work Program Print Summary PDF. When a user clicks **Print all** on the Summary tab of a work program, the system checks for an active template and uses it to produce the PDF instead of the default built-in layout.

Navigate to **Settings → Print Summary Templates** in the admin menu.

![Print Summary Templates admin page](media/admin_guide_lototo_print_summary_templates.png)

## How It Works

1. An administrator uploads an `.xlsx` file that defines the PDF layout — headers, sections, labels, and field placeholders
2. The template is marked as **Active**
3. When any user generates a print summary PDF, the application fills the template with the work program's real data and produces a fully populated PDF
4. The PDF is then merged with the P&ID sketch PDFs captured from the model, with the template pages appearing first

Only one template can be active at a time. Uploading a new template does not automatically activate it — you must explicitly set it as active.

## Template Actions

| Action         | How                                                                                                                                                                 |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Upload**     | Click **Upload File** → select an `.xlsx` file → file is uploaded to storage and registered in the database                                                         |
| **Set Active** | Click the checkbox in the **Active** column on any inactive row → that template becomes active, the previous active template is deactivated                         |
| **Preview**    | Click any row (not the delete or download button) → a popup opens rendering each sheet of the Excel file as HTML so you can inspect the layout before activating it |
| **Download**   | Click the download icon on a row → downloads the original `.xlsx` file                                                                                              |
| **Delete**     | Click the trash icon → confirmation dialog → removes the record from the database                                                                                   |

## Template Preview

Clicking a row opens a preview popup that renders the `.xlsx` file as HTML, faithfully reproducing:

- Merged cells, column widths, and row heights
- Font styling (name, size, bold, italic, colour)
- Cell background fills (solid colours, theme colours with tint)
- Borders (thin, medium, thick)
- Horizontal and vertical text alignment

If the template has multiple sheets, tab buttons appear at the top of the popup for switching between them.

## Designing a Template

Templates are standard Excel (`.xlsx`) files. The key feature is **field tokens** — any cell whose value is `#FieldName` is automatically replaced with the corresponding work program data when the PDF is generated.

### Field Tokens — `#FieldName` and `##FieldName`

Place a token like `#WorkProgramName` in any cell and it will be replaced at generation time. Tokens are case-insensitive.

Any metadata field names and master detail column names configured for the work program are also supported as tokens, using the same `#FieldName` syntax. If a token cannot be resolved, the raw token text is displayed as-is in the PDF.

There are two token formats with different rendering behaviour for dropdown-type metadata fields:

| Token format    | Behaviour for dropdown fields                                                                         | Behaviour for all other fields |
| --------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------ |
| `#FieldName`    | Selected value shown in the cell; all dropdown options listed as an interactive checklist below the row, with the selected option pre-checked | Value substituted inline       |
| `##FieldName`   | Selected value shown inline only — no checklist is generated                                          | Value substituted inline       |

Use `##FieldName` (double hash) when you want a dropdown field to appear as plain text in the PDF without the accompanying option checklist.

### Control Tokens

Two tokens have special rendering behaviour and are **not** replaced with data values:

| Token                                       | Effect in the generated PDF                                                                                          |
| ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `#PIDSketches`                              | A list of all P&ID drawing names linked to the work program is printed below this cell                               |
| `#Comment` / `#Kommentar` / `#Comentariu`  | An interactive, editable text field is placed over the entire cell in the PDF — users can type into it inside a PDF reader |

All three comment tokens behave identically — use whichever matches your template language.

### Embedded Images

Any images embedded directly in the Excel template (inserted into cells via **Insert → Picture**) are rendered into the generated PDF at the position and size defined in the template. This allows logos, diagrams, icons, or any other static graphic to be included in the report layout without needing to be part of a data token.

### Interactive Fields

- **Boolean cells** — Any Excel cell containing a checkbox is rendered as an interactive checkbox in the PDF. Users can tick or untick it in a PDF reader
- **Dropdown fields (`#FieldName`)** — When a single-hash token maps to a dropdown-type metadata field, the selected value is shown in the cell and all available options are listed as an interactive checklist below the row, with the selected option pre-checked
- **Dropdown fields (`##FieldName`)** — When a double-hash token maps to a dropdown-type metadata field, only the selected value is shown inline — no checklist is generated
- **`#Comment` / `#Kommentar` / `#Comentariu` cells** — Rendered as fillable text fields sized to the full cell dimensions

### Sheet Naming

Name your Excel sheets to control what data is injected into each one:

| Sheet name keyword                       | Data injected                                            |
| ---------------------------------------- | -------------------------------------------------------- |
| Contains `valve`                         | Valves list                                              |
| Contains `blind`                         | Blind list                                               |
| Contains `electrical`, `edrive`, `drive` | Electrical drives list                                   |
| Contains `drain`                         | Drains list                                              |
| Contains `hose`                          | Hose list                                                |
| Contains `other`                         | Other list                                               |
| Contains `preparation`                   | Preparation steps table                                  |
| Contains `decommissioning` or `decom`    | De-commissioning steps table                             |
| Contains `repair`                        | Repair steps table                                       |
| Contains `commissioning`                 | Commissioning steps table                                |
| Any other name                           | General template sheet — field tokens only, no list data |

Steps data is always rendered on any sheet named **Work Program** regardless of whether it matches a steps keyword.

Sheets are rendered in their Excel tab order. All other Excel formatting (colours, fonts, borders, merged cells) is preserved in the generated PDF.

# Languages

The **Languages** page allows administrators to manage all language translations used throughout the application. Administrators can download all current translations as an Excel file, edit them offline, upload updated or new languages via Excel, edit individual translations in the grid, and configure default language settings.

![The languages administration page](media/admin_guide_languages_page.png)

## Overview

- The Language Management system uses a **two-table architecture** in the database:
  - **StandardTranslations**: Base translations loaded from the `translations.json` file at application startup. These serve as read-only templates and cannot be edited through the UI.
  - **CustomTranslations**: User-editable translation overrides that take priority over standard translations. These are managed entirely through the admin interface.

- Both tables share the same schema: each row has a `key` (the translation key) and a `translations` column containing a JSON object mapping language codes to translated values. For example:

  ```json
  { "en-US": "Add Language", "ko": "언어 추가", "ar": "إضافة لغة" }
  ```

- Administrators can set a **default language** that serves as the fallback for all translations. This setting is stored in the `Dictionary` table under the `SYSTEM_CONFIG` language with the key `DEFAULT_LANGUAGE`.

- Users can select their preferred language from available languages, which persists across sessions via the `Language` field on their account record. Users may also select "Default" to always follow the system default language.

- Translation priority system ensures the best available translation is displayed:
  1. Custom translation in current language (highest priority)
  2. Standard translation in current language
  3. Custom translation in default language
  4. Standard translation in default language
  5. `en-US` as the ultimate hardcoded fallback
  6. `(Missing: <key>)` placeholder if no translation exists anywhere

## Standard Translations and Startup Initialization

Standard translations are the baseline dictionary provided with the application. Their source is `backend/src/Toolkit.Data/Data/Seeding/translations.json`, with this structure:

```json
{
  "Add Language": { "en-US": "Add Language", "ko": "언어 추가" },
  "Cancel": { "en-US": "Cancel", "ko": "취소" }
}
```

### How Standard Translations Are Loaded

The backend build embeds `translations.json`. At every start, the database seeder (`ToolkitDbSeeder`) reads this file, inserts new keys into the `StandardTranslations` table, and updates changed text. Keys that the file no longer contains stay in the table.

### Updating Standard Translations

1. Edit `backend/src/Toolkit.Data/Data/Seeding/translations.json` directly.
2. Rebuild and restart the application. The translations will be automatically loaded from `translations.json` at startup.

## Features

### 1. Language Selection

**Default Language**

- Defines the base language used as the fallback when translations are missing in other languages.

- Can be set from the dropdown at the top of the Languages page.

- Serves as the template for creating new custom languages (new languages are initialized with all keys from the default language's standard translations).

**Edit Language**

- Select which language dictionary to view and edit in the translation grid.

- Displays all translation key-value pairs, merging both standard and custom translations.

- Both standard and custom languages appear in this dropdown.

### 2. **Translation Grid**

- Displays a merged view of all translation keys and their corresponding values for the selected language.

- **Visual Indicators**:
  Rows with:
  - **Green dot**: Custom translation (editable, stored in CustomTranslations table). Custom values are displayed in **bold**.
  - **Yellow/Orange dot**: Standard translation (from StandardTranslations table). These rows indicate keys that only exist in standard translations and have no custom override yet.

![Visual Indicator](media/admin_guide_language_dot_indicator.png)

- **Key Column**: Contains the translation key (read-only, cannot be edited).

- **Value Column**: Contains the translated text (editable).

- **Editing Translations**:
  - Click the edit icon on a row to modify its value.
  - Edits to standard translation rows (**Yellow/Orange dot**) automatically create a custom override, promoting them to the CustomTranslations table for the edited language. The dot changes to green after saving.
  - All edits are persisted immediately upon saving the row.

- Features a filter row for quick searching of specific translations by key or value.

### 3. Language Import / Export

The **Language Import / Export** popup provides two actions: downloading all current translations and uploading updated or new translations. Click the **Upload languages as xlsx** button in the header to open it.

**Download current languages as xlsx**

1. Click the **Download current languages as xlsx** button inside the popup.

2. An Excel file is generated containing:
   - A `key` column with all translation keys
   - One column per language code (e.g., `en-US`, `de-DE`) with the current merged translation values (custom overrides take priority over standard translations)

**Upload languages as xlsx**

1. Click the **Upload languages as xlsx** button inside the popup to select an `.xlsx` or `.xls` file.

2. The import begins immediately upon file selection — no extra confirmation step is needed.

3. A **two-level progress bar** is displayed showing:
   - Which language is being imported and the language count (e.g., "Importing de-DE (2/4 languages)")
   - Key-level progress within the current language (e.g., "400 / 1200 keys")
   - A main progress bar for key progress and a thin bar for overall language progress

4. When complete, the popup displays:
   - A **success message** with the list of imported language codes, or
   - An **error message** if something went wrong
   - Any **invalid column names** that were skipped (shown in red)

**Important behavior**:

- **No languages are ever deleted** through this dialog. If a language exists in the system but is not present in the uploaded file, it remains untouched.
- If a language code is renamed in the Excel (e.g., `en-US` changed to `en-usa`), the renamed code is treated as a new language. The original `en-US` is not affected.
- Existing languages in the file are updated (custom translations are upserted). New language codes in the file are added.
- Language codes must be valid (2-3 letter base, optionally followed by a region code like `-US`, `-BR`). Invalid codes are shown to the user and skipped during import.

### 5. Delete Custom Language

Remove a custom language dictionary that is no longer needed:

1. Select the custom language to delete from the **Edit Language** dropdown.

2. The **Delete Custom Language** button appears (only visible when a custom language is selected, not for standard-only languages).

3. Click **Delete Custom Language**.

4. Confirm the deletion in the popup dialog.

5. The language is removed from the CustomTranslations table and the selection reverts to the default language.

**Note**: Deleting a custom language removes all custom translation overrides for that language. Standard translations are not affected.

## Managing Languages

### Update Existing Custom Translations (In detail)

1. Select the custom language to edit from the **Edit Language** dropdown.

2. Use the filter row to search for specific translations.

3. Click the edit icon button to edit the row.

4. Modify the value and click the **Save** button.

![Editing translations](media/admin_guide_languages_editing.gif)

### Set Default Language

1. Select the desired language from the **Default Language** dropdown.

2. The system updates the default language immediately.

3. The default language setting is stored in the database and persists across application restarts.

### Understanding Translation Priority

When the application displays a translation, it follows this priority order:

1. **Custom translation in user's current language** - User-specific overrides
2. **Standard translation in user's current language** - Base translations from the JSON/Excel file
3. **Custom translation in default language** - Fallback custom translations
4. **Standard translation in default language** - Fallback base translations
5. **`en-US` hardcoded fallback** - If the default language itself is not found
6. **`(Missing: <key>)` placeholder** - Displayed if no translation exists for the key in any source

This ensures users always see the most appropriate translation available.

## Excel File Format (Custom Language Import/Export)

Both the download and upload functions use the same **rotated format** where each language is a column:

| key           | en-US         | ko        | de-DE           |
| ------------- | ------------- | --------- | --------------- |
| Cancel        | Cancel        | 취소      | Abbrechen       |
| Save          | Save          | 저장      | Speichern       |
| Select a file | Select a file | 파일 선택 | Datei auswählen |

### Requirements

- The Excel file **must contain a header row** with a `key` column and one or more language code columns.
- Language codes in column headers must be valid (e.g., `en-US`, `ko`, `de-DE`, `ar`). Invalid codes are skipped with a warning.
- Each row represents a single translation entry.
- Keys must match the standard translation keys exactly (do not modify keys).
- Values should contain your custom translations.
- The system processes **all** language columns in the file at once.
- **No languages are deleted** during import. Only additions and updates are performed.

## Right-to-Left (RTL) Language Support

The application automatically supports Right-to-Left languages such as Arabic (`ar`) and Hebrew (`he`).

- When a language with RTL directionality is selected, the interface layout automatically adjusts to display content from right to left.

- Text alignment, menu positioning, and component layouts are automatically reversed to provide a natural reading experience.

- RTL support is determined by the language code. The following language prefixes trigger RTL mode: `ar`, `ar-SA`, `he`, `he-IL`, `fa`, `fa-IR`, `ur`, `ur-PK`.

- All UI components, including DevExtreme grids and form controls, adapt to the RTL layout automatically. The DevExtreme library is configured with `rtlEnabled: true` when an RTL language is active.

- RTL detection works by prefix matching: any language code starting with one of the RTL prefixes (e.g., `ar-EG`, `he-IL`) will trigger RTL mode.

![RTL](media/admin_guide_languages_rtl.gif)

## Technical Architecture

This section provides technical details about how the multi-language system is implemented for developers and advanced administrators.

### Database Schema

The translation system uses two database tables in the `common` schema:

**StandardTranslations**

| Column       | Type          | Description                                  |
| ------------ | ------------- | -------------------------------------------- |
| id           | Int (PK)      | Auto-incrementing primary key                |
| key          | NVarChar(500) | The translation key (e.g., "Add Language")   |
| translations | NVarChar(Max) | JSON object mapping language codes to values |

**CustomTranslations**

| Column       | Type          | Description                                  |
| ------------ | ------------- | -------------------------------------------- |
| id           | Int (PK)      | Auto-incrementing primary key                |
| key          | NVarChar(500) | The translation key (e.g., "Add Language")   |
| translations | NVarChar(Max) | JSON object mapping language codes to values |

Both tables store translations in a **JSON column** format. Each row represents one translation key, and the `translations` column contains a JSON object with language codes as keys:

```json
{ "en-US": "Cancel", "ko": "취소", "ar": "إلغاء", "de-DE": "Abbrechen" }
```

The default language setting is stored in the **Dictionary** table:

- `language`: `"SYSTEM_CONFIG"`
- `key`: `"DEFAULT_LANGUAGE"`
- `value`: The language code (e.g., `"en-US"`)

### Client-Side Architecture

**LanguageProvider Context** (`frontend/src/context/LanguageContext.tsx`):

- Wraps the entire application and provides the current dictionary, language, and direction to all components via React Context.
- On initialization, loads the default language and its merged dictionary.
- When a user's language preference is loaded from their account, fetches the corresponding merged dictionary.
- Exposes a `useLanguage()` hook that returns: `language`, `setLanguage`, `dictionary`, `isLoading`, `direction` (`"ltr"` or `"rtl"`), and `isRTL`.
- Exposes a `useTranslate()` hook that returns the key itself for any key that is not found.
- When the language changes, it sets the `dir` attribute on `<html>` to `"rtl"` or `"ltr"` and configures DevExtreme's `rtlEnabled` setting accordingly.

**DevExtreme Locale Integration** (`frontend/src/lib/useDevExtremeLocale.ts`):

- Loads the appropriate DevExtreme localization bundle (e.g., `ar`, `ko`, `de`) for built-in widget strings.
- Applies translation overrides from the application dictionary for common DevExtreme strings (e.g., "No data", "Search", "Cancel", filter operation labels).
- Sets the DevExtreme locale to match the user's language for proper date/number formatting.

### How Translations Are Used in Components

All translatable strings in the application are accessed through the `dictionary` object provided by the `useLanguage()` hook:

```tsx
const { dictionary } = useLanguage();

// Usage in JSX
<h1>{dictionary["Language Management"]}</h1>
<Button text={dictionary["Save"]} />
```

Translation keys are plain English strings (e.g., `"Add Language"`, `"Cancel"`, `"Failed to load translations"`). The dictionary is a key-value map where the key is the English string and the value is the translated string in the user's current language.

### Translation Validation Tests

The project includes automated tests that check `backend/src/Toolkit.Data/Data/Seeding/translations.json`:

- `backend/tests/Toolkit.Api.Tests/Translations/ShippedDictionaryUsageTests.cs` fails for a key in the file that no source code references. Keys that must ship without a reference go into `ShippedDictionaryAllowList.txt` in the same folder.
- `frontend/__tests__/translations-duplicate-keys.test.ts` fails for a key that occurs more than once in the file.

Run translation tests with: `dotnet test backend/tests/Toolkit.Api.Tests --filter ShippedDictionaryUsageTests` and `cd frontend && npx vitest run __tests__/translations-duplicate-keys.test.ts`

# General Settings

Navigate to **Settings** at the bottom of the navigation bar

### Change the Toolkit Theme

- Users can personalize the Toolkit's appearance by selecting a theme from a dropdown menu.

- Each theme modifies the application's visual style to match the selected design.

- Once a theme is chosen, the selection is saved.

- The selected theme is automatically applied across all future sessions for that user.

![Setting Theme](media/admin_guide_set_theme.gif)

### **Configure Language Options**

Users can select their preferred language from the **Language Settings** section.

- Use the dropdown menu to select from available languages (both standard and custom).

- The selected language is tied to the user account and is saved automatically.

- Users may select **"Default"** to always follow the system's default language. If the administrator changes the default language, users on "Default" will see the updated language automatically.

- The language preference is applied across all future sessions.

- If a translation is missing in the selected language, the system will fall back to:
  1. Standard translation in the selected language
  2. Custom translation in the default language
  3. Standard translation in the default language
  4. `en-US` as the ultimate fallback

- When a Right-to-Left language (e.g., Arabic, Hebrew) is selected, the entire interface layout automatically mirrors to RTL. See the [Right-to-Left (RTL) Language Support](#right-to-left-rtl-language-support) section for details.

**Note**: Only languages that have been configured (either as standard translations in the Excel file or as custom translations created by administrators) appear in this dropdown. For information on creating and managing languages, see the [Languages](#languages) section.

### Signature Settings

The Signature Settings section on the Settings page allows administrators to manage their own personal Interactions email signature.

- This signature is automatically included in the default message body when composing an Interactions email.

- Automatically appends a consistent sign-off to each email.

- Updates to the signature are saved to the user's profile and persist across sessions, eliminating the need to re-enter it each time.

![Setting Theme](media/user_guide_signature.png)

### Company Settings

The Company Settings section allows administrators to customize branding for the Toolkit application by setting a company name and uploading a company logo.

![Company Settings](media/admin_guide_company_info.png)

#### Company Name

- Enter your organization's name in the **Company Name** text field.

- The name will automatically save.

- This company name will appear on outgoing communications and throughout UI elements.

- Use the **Clear** button (X icon) to quickly remove the current company name if needed.

- A "Saving..." indicator appears briefly to confirm that your changes are being saved.

#### Company Logo

- Upload a custom logo to represent your organization throughout the Toolkit interface.

- The logo uploader supports all standard image formats (PNG, JPG, JPEG, GIF, SVG, etc.).

- You can either **drag and drop** an image file into the upload area or **click to browse** and select a file from your computer.

- Once selected, the logo is automatically uploaded and saved.

- A preview of your uploaded logo is displayed below the uploader for verification.

- The selected logo persists across all sessions and is visible to all users.

**Best Practices:**

- Use a **square or wide image** for optimal display across different UI components
- **Transparent PNG** format is recommended for the best visual results
- Ensure the logo has sufficient contrast against both light and dark backgrounds
- Keep file sizes reasonable (under 2MB) for faster loading

# Automatic Database Backups

The Toolkit can back up its database automatically before it applies a database update (migration). This backup is the recovery point if an update fails.

### How It Works

- Set the environment variable `DATABASE_BACKUP_PATH` to a directory **on the SQL Server host** (not in the Toolkit container). SQL Server writes the backup file to this directory. The directory must exist.
- The Toolkit makes a backup only when a database update is pending. A normal restart makes no backup.
- File name: `{database}_pre-migration_{date-time}Z_{container}.bak` (time in UTC).
- The Toolkit deletes no backup files. Remove old files yourself.
- If the backup fails, the Toolkit does not update the database and does not start.

### Limit on Repeated Backups

If a database update fails, the container restarts and tries the update again. To prevent one backup per restart, the Toolkit reads the SQL Server backup history. If a backup of this database is less than **10 minutes** old, the Toolkit makes no new backup.

### Necessary Permissions

The database login of the Toolkit needs:

1. **Backup permission** on the Toolkit database (`db_backupoperator` or `db_owner`).
2. **Read access to the backup history** in `msdb`. Without this access, the Toolkit logs a warning and makes a backup on **every** restart with a pending update. This can fill the backup directory quickly.

The SQL Server service account also needs write access to the backup directory.

Example (replace `toolkit` with the Toolkit login and `ToolkitDb` with the database name):

```sql
USE [ToolkitDb];
ALTER ROLE [db_backupoperator] ADD MEMBER [toolkit];

USE [msdb];
CREATE USER [toolkit] FOR LOGIN [toolkit];  -- skip if the user already exists
GRANT SELECT ON [dbo].[backupset] TO [toolkit];
GRANT SELECT ON [dbo].[backupmediafamily] TO [toolkit];
```

**Note**: Azure SQL Database does not support this backup. Do not set `DATABASE_BACKUP_PATH` there.

# Legacy Prisma Migrations

Older Toolkit versions (Next.js app) updated the database with Prisma. The current Toolkit contains all these old updates. At start, it applies the old updates that the database does not have yet, then the current updates. Thus you can upgrade from any older version directly. A pending old update also triggers the automatic backup.

The Toolkit reads the table `dbo._prisma_migrations` to find the old updates that the database already has.

### Start Fails: "The database holds tables but no migration history"

The database has Toolkit tables, but no update history. The Toolkit cannot find the version of the database. It applies no update and does not start.

To fix:

1. Find the Toolkit version that created the database.
2. Create the table `dbo._prisma_migrations` and insert one row for each old update that this version contains. Ask support for the list of update names and the script for your version.
3. Restart the Toolkit.

### Start Fails: "Prisma migration(s) … failed on an earlier Toolkit version"

An old update started but did not complete. Repair the database, then set `finished_at` or `rolled_back_at` for that row in `dbo._prisma_migrations`. Then restart the Toolkit. Contact support if you are not sure.

---

# Toolkit App for UDiTH — Toolkit User Guide

# Toolkit User Guide

Welcome to the user guide for UDiTH Toolkit App.

# Homepage

The **Homepage** provides an overview of four sections: **Dashboard**, **Calendar**, **Subscriptions**, and **Notifications**, offering quick access to metrics, schedules, updates, and alerts to keep users informed and organized.

![The homepage](media/user_guide_homepage.png)

## Dashboard

The **Dashboard** offers a clear overview of punch items. Users can see the number of open, closed and past due punch items. Users can also see the overall workflow progress for the punch items.

![The dashboard](media/user_guide_dashboard.png)

## Calendar

The **Calendar** offers a timeline view, helping users track schedules, deadlines, and key project milestones.

![The calendar](media/user_guide_calendar.png)

## Subscriptions

The **Subscriptions** section enables users to monitor their subscribed punch items, providing quick access to key updates. Users can choose to view detailed information about each punch item or navigate directly to editing its fields, making it easy to stay informed and manage items efficiently.

## Notifications

The **Notifications** panel displays alerts and updates related to punch items, workflows, and system activities, ensuring users stay informed about important changes and actions requiring attention.

# Tickets

Users have three options to select from under the Tickets tab in the navigation bar: **Ticket List, Kanban Board & Progress Tracking**. Each will be detailed below. Features since Toolkit 2026.4.0 not documented yet here are ticket types and ticket parenting. 

![Punchlist options](media/user_guide_punchlist_options.gif)

## Ticket List

The **Ticket List** page provides users with an overview of all punch items associated with a project in a tabular format. It provides an interface for users to view, create, edit and delete punch items. It also offers tools for sorting, grouping, and customizing columns, as well as options to generate PDF reports and export data as XLSX files.

![The punch items page](media/user_guide_punch_items.png)

### General Page Actions

- Users can sort the punch items in ascending/descending, and alphabetical order by clicking the column headers of the table.

- Users can group the punch items by column by clicking on a header and dragging it to the section that says "**Drag a column header here to group by that column**".

- Users can utilize the drag-and-drop column picker to add and remove columns from the table.

- Users can generate a detailed PDF report for selected punch items by clicking the **"Generate PDF Report"** button (located to the right of the trash icon). To include a punch item in the report, ensure the checkbox to the left of its row is selected.
- Users can export data to an XLSX file by clicking the **"Export"** button (located to the right of the **"Generate PDF Report"** button). A prompt will appear, allowing users to choose between exporting the entire table or only the selected rows.

![Punch items page actions](media/user_guide_punch_items_actions.gif)

### Filter Presets

- Users can **select a saved filter preset** from the dropdown menu above the punchlist table. Selecting a preset instantly applies its saved filters, column layout, and sorting.

- To **save your current view as a new preset**, click the **Save Preset** button in the table’s toolbar. Enter a name for the preset, and it will be added to the preset selector for future use.
  - **Note:** If a preset with the entered name already exists, you will be prompted to enter a different, unique name.

- Users can **delete any preset** by selecting it in the dropdown and clicking the delete (trash) button.

- Users can **customize the table’s columns** using the **Column Picker**. Columns can be shown/hidden, and resized as needed.

- Users can **customize the table’s columns** using the **Column Picker**. Columns can be shown/hidden, and resized as needed.

- Users can **set a default preset** by selecting the radio/checkbox option next to a preset in the dropdown. The default preset is preserved between sessions and automatically applied when the page is reopened.

![Punchlist Table UI](media/user_guide_punchlist_preset.gif)

### Creating a new punch item

Users have four options when it comes to creating new punch items: **Create Blank, Create with Comment, Create from Template & Upload UPVF**. All four options can be accessed by clicking the drop-down arrow on the "**Create Ticket**" button and selecting the desired one.

1. Create Blank

- Clicking on the "Create Blank" option will open a popup prompting the user on if they would like to proceed with creating a new punch item.
- This punch item will be created with no information and it will require the user to navigate to the details page of that punch and populate it with information.

![Creating a new punch item with a blank template](media/user_guide_punch_item_creation_blank_template.gif)

2. Create with Comment

- To create a punch item with a comment, users must first select at least one object in the model. If no object is selected, the application will display a popup notifying the user of an invalid selection.
- When a valid selection is made, a popup window will appear, showing the number of objects selected and a dropdown menu for users to choose their desired object. The selected object will be the one used for attribute mappings when the **"Create"** button is clicked.
- The popup includes three textboxes: **Unit No**, **Pipeline**, and **Discipline**:
  - **Unit No**: Auto-populated from the Unit System attribute of the selected object and cannot be modified by the user.
  - **Pipeline** and **Discipline**: Can be filled in by the user and will be included in the comment of the created punch item.

- To finalize and create the punch item, users can click the **"Create"** button.

![Creating a new punch item with a comment](media/user_guide_punchlist_create_with_comment.gif)

3. Create from Template

The **Create from Template** option lets you create multiple tickets at once by uploading a spreadsheet file. It also supports updating existing tickets in bulk. The process follows a guided three-step workflow.

**Supported File Formats:**

- **CSV (.csv)** — Comma-separated values format
- **XLSX (.xlsx)** — Microsoft Excel spreadsheet format

#### Step 1 — Download the Template

When you select **"Create from Template"**, a popup appears with a download link. Click it to get a template file containing all available ticket fields as column headers. The first column is **TicketId**, followed by every detail field configured for your project.

Fill in the template with your ticket data — each row represents one ticket. You may leave the **TicketId** column empty if you plan to create all tickets as new (the system will assign IDs automatically).

#### Step 2 — Upload Your File

Click the **"Upload"** button in the popup and select your completed CSV or XLSX file. The system automatically validates the uploaded data:

- **Column names** must match existing ticket fields. Unrecognized columns are flagged as errors.
- **Data types** are checked — date fields must contain valid dates, number fields must contain numbers.
- **String length** — values exceeding 1,000 characters are rejected.

If validation errors are found, they are displayed so you can correct the file and re-upload.

#### Step 3 — Choose an Import Mode

After a successful upload, you are presented with three import mode cards:

| Mode                      | What it does                                                                                             |
| ------------------------- | -------------------------------------------------------------------------------------------------------- |
| **Add if new**            | Creates tickets only for rows that do not already exist. Rows matching an existing ticket are skipped.   |
| **Overwrite if existing** | Updates existing tickets with the uploaded data. Rows that do not match any existing ticket are skipped. |
| **Add & Overwrite**       | Creates new tickets for new rows and updates existing tickets for matching rows.                         |

The system detects duplicates automatically by comparing the **TicketId** column in your file against tickets already in the database.

#### Advanced Options

Below the mode cards, an **"Advanced"** link reveals additional import settings:

- **Import all tickets as new** — When checked, every row in the file is created as a brand-new ticket with a system-generated ID, regardless of any TicketId values in the file. No duplicate detection runs in this mode.

- **Custom identifier field** — Instead of matching duplicates by TicketId, you can select any ticket field (e.g., Tag Number, Description) from the dropdown. The system will then match rows against existing tickets using that field's value. All three import modes remain available when a custom identifier is selected.

  > **Note:** When a custom identifier field is selected, any TicketId values in the file are ignored — matching is based solely on the chosen field.

#### Preview and Confirm

After selecting your import mode, click **"Import"**. A preview popup appears summarizing what will happen:

- **Tickets to add** — New tickets that will be created.
- **Tickets to overwrite** — Existing tickets that will be updated with new values.

Review the summary and click **"Confirm"** to proceed with the import.

#### Import Log

Once the import completes, a results popup shows how many tickets were created, overwritten, skipped, or failed. You can download a detailed **import log** file that lists the outcome for every row, including:

- Which tickets were created or overwritten
- What field values changed (showing old and new values for overwrites)
- Any rows that were skipped or failed, with reasons

![Creating a new punch item from template](media/user_guide_punchlist_create_from_template.gif)

4. **Upload UPVF**

- This option opens a popup prompting the user to upload a UPVF file.
- Once the file is uploaded and the **"Upload"** button is clicked, punch items will be created using the information from the file.

## Ticket Details

Users can access the user form of a specific punch item by double-clicking on a row in the main punchlist table. This action will open the page shown below.

![Punchlist details user form](media/user_guide_punchlist_details.png)

### Details

Upon opening this focused view, users are automatically directed to the **Details Tab**. This tab provides access to all punchlist fields which are defined by the administrator.

- Users can edit the punch item’s information directly within this tab, except for the textboxes in the top row.
- The fields in the top row are system-generated and updated by the program. These fields are not editable by users to ensure data integrity.

### Components

The **Components Tab** allows users to manage 3D and 2D components associated with a specific punch item, including adding, deleting, and interacting with the model.

- **Add and Delete Components:**
  - Users can add components to the punch item or delete them as needed.
  - To add all components currently selected in the model, click the **"Add Components"** button. This will add both 3D and 2D components currently selected.
  - To remove selected components from the punch item, click the **"Remove Selected Components"** button. This will delete both 3D and 2D components currently selected.
  - Individual components can be deleted from the table by clicking the trash icon at the end of the corresponding row.

- **Model Interaction:**
  - Users can interact with the model to focus on specific components.
  - In the top left corner, there is a **combo dropdown and button component** where users can define the desired model operations. After selecting a row from the table, these operations can be executed by clicking the button or by clicking on the row itself.

- **Table Configuration**
  - Users can configure their table view using the column picker.
  - The customized configuration is saved so that each user sees their own settings when they return to the table.
  - The first time a user accesses this page, the table configuration is set to the default based on the administrator's settings.
  - From that point on, any adjustments made via the column picker become the user's custom configuration.

- **Additional Options:**
  - **Clear Filter:** Removes any filters applied to the model.
  - **Select and Highlight All:** Selects and highlights all 3D components listed in the table.

![Punchlist details components](media/user_guide_punchlist_components.png)

### Attachments

The **Attachments Tab** allows users to manage files and attachments associated with a specific punch item.

- **Automatic Folder Creation:**
  - When a punch item is created and opened, a folder in UDiTH App is automatically generated, named after the punch ID.
  - Users can add files directly to this folder through the UDiTH App (e.g., photos, markups) and save it by clicking the **"Save Folder"** button.

- **UPVF History:**
  - Each time the folder is saved, a copy is stored. Previous versions of the folder can be viewed in the **UPVF History** section. They are organized by date, specifically by the year and the month they were saved.
  - Users can also open any of the previous folders in UDiTH App by clicking on that specific item.
  - The UPVF History can be toggled on or off using the **"UPVF History"** button located to the left of the **"Save Folder"** button.

- **Folder Updates:**
  - Whenever this punch item is opened in the detailed view, the application retrieves and displays the most recent version of the UDiTH App folder in the file tree.

- **Adding Files and Screenshots:**
  - Users can add files to the punch item by clicking the **"Add File"** button.
  - The attachments will appear in the main section of this tab, and users have the ability to click on it and download it locally. They also have the ability to delete it by clicking on the "X" in the top right corner of it.
  - Screenshots of the model can be taken using the **"Screenshot"** button.
    - Users can specify whether the screenshot should be set as the **initial screenshot** or the **final screenshot** of the punch item.
    - If available, the initial and final screenshots are displayed in the rightmost section of this tab.

- **Report Generation:**
  - Users can generate a detailed report for the punch item directly from this tab.
  - This report includes all details of the punch item and is identical to the report generated from the main punchlist table but is only for this specific punch item.

![Punchlist details attachments](media/user_guide_punchlist_attachments.png)

### Approvals

In the **Approvals Tab**, users can manage workflows for a punch item.

- **Assigning Workflows:**
  - Click the **"Assign Workflow"** button to open a popup prompting the user to select a workflow from a list of available options, defined by the application administrator.
  - After selecting a valid workflow, click the **"Add Workflow"** button to assign it to the punch item.

- **Workflow Overview:**
  - Once added, the workflow will appear on the page, outlining all the steps required to complete the punch item along with their details.
  - Users can assign multiple workflows to a single punch item and toggle between them using the dropdown menu to the right of the **"Assign Workflow"** button.

- **Step Status Indicators:**
  - Each workflow step is marked with a colored circle to indicate its status:
    - **Grey:** Not started.
    - **Yellow:** In progress.
    - **Green:** Approved.
    - **Red:** Disapproved.
  - Steps must be completed in sequential order and cannot be skipped.

- **Additional Actions:**
  - **Show Flowchart:** Click the **"Show Flowchart"** button to display a flowchart representation of the workflow. Click the button again to hide it.
  - **Delete Workflow:** Remove a workflow by clicking the **"Delete Workflow"** button.
  - **Show Hidden Steps:** Toggle hidden steps on or off using the **"Show Hidden Steps"** button.

- **Interacting with Workflow Steps:**
  - To interact with a step users can click on it directly. This will open the popup seen below. From here a user has the ability to approve or disapprove this step and add notes on their decision. Once again, steps must be completed sequentially so if a step that has not been started yet is selected, it will not have the ability to be approved or disapproved.

![Punchlist details approvals pop-up](media/user_guide_punchlist_approvals_popup.gif)

### Additional Features

- **Back Button:**
  - The focused view includes a **Back** button in the top-left corner of the navigation bar. Clicking this button will redirect users back to the previous page they were on.

- **Activity Chat:**
  - An activity chat is visible on the right side of the page, tracking all changes and interactions related to the punch item.
  - Users can also send messages through the activity chat to share additional information as needed.
  - The activity chat can be toggled on or off using the **chat icon** in the top-right corner.

![Punchlist details approvals page](media/user_guide_punchlist_approvals.png)

## Kanban Board

The **Kanban Board** allows users to view, sort, filter, organize, and plan the execution of punch items effectively.

### Grouping Options

1.  **Field-Based Grouping:**
    - Users can group punch items by specific fields using the **dropdown menu** in the top left corner.
    - All punch items with the same value for the selected field will be grouped together in the main section.
    - Each punch item is represented by a card containing information defined by the application administrator.

2.  **Workflow-Based Grouping:**
    - When grouping by **Workflow**, an additional **dropdown menu** appears, allowing users to specify which workflow to group by.
    - In this view:
      - List headers represent the individual steps of the selected workflow.
      - Cards (tickets) are placed in lists corresponding to their current workflow step.
    - The workflow view within the Kanban board provide users a focused grouping of tickets implementing the selected workflow.

![Workflow dropdown](media/user_guide_kanban_dropdown.png)

### Workflow Step Advancement

- Users can adjust a ticket's current step by dragging and dropping the card a maximum of one step.
- Upon dragging a card, a popup will appear prompting the user to either approve or disapprove the step.

- Users can advance a ticket to the next step of the workflow by dragging and dropping the card a step forward:

![Board actions](media/user_guide_kanban_drag.gif)

- Alternatively, users can set back a tickets workflow progress by dragging and dropping the card a step backward:

![Board actions](media/user_guide_kanban_backwards.gif)

- For streamlining the approval / denial actions for multiple tickets, utilize the checkbox and then perform the actions to apply changes to the selected tickets:

![Simultaneous actions](media/user_guide_kanban_simul.gif)

- Actions configuring a ticket's workflow status require confirmation to persist.
- Users are given the option to attach a note to a step action approval / denial, detailing the decision with supplementary information.
- The approval / denial of workflow steps occurs in the confirmation popup. Users finalize the action by utilizing the confirmation buttons.
  - Clicking the edit button on the ticket card instead of utilizing the drag-and-drop feature makes both actions simultaneously accessible.

![Both at once](media/user_guide_kanban_both.gif)

### Kanban Board & Approvals Tab Sync

- The workflow actions performed for a ticket in the Kanban board directly impacts the Approvals tab within the same ticket.
- When navigating to the ticket's Approvals tab (Double-clicking on the card in the kanban board for a streamlined flow), workflow step changes are synced to those performed in the kanban board.

![Approvals tab](media/user_guide_approvals.png)

### Additional Features

- **Navigating to Ticket Details:**
  - Users can double-click on a card to open the detailed view of the selected punch item.

![Planning page](media/user_guide_planning.png)

## Progress Tracking

The **Progress Tracking** page provides a comprehensive overview of all punch items, including the workflows assigned to them, their current workflow step, and the overall progress of each workflow.

- **Overview Panel:**
  - On the right side of the page, an overview displays the total number of open and closed workflows across the application.

- **Exporting Data:**
  - Users can export the entire table or selected rows as an XLSX file by clicking the **Export** dropdown button.

- **Customizing Columns:**
  - The visible columns in the table can be customized using the **Column Picker** button, located to the left of the search bar.

- **Navigating to Ticket Details:**
  - Users can open the detailed view of a punch item by double-clicking on a row.

![Workflow tracking page](media/user_guide_workflow_tracking.png)

# General Settings

The General Settings page allows users to personalize their experience within the application by configuring visual themes, interface language, and a custom email signature.
To access this page, click the settings gearwheel icon at the bottom of the navigation bar.

## Theme Settings

Users can personalize the visual style of the application by choosing a theme from the theme settings section on the settings page.

- Use the dropdown menu to pick a theme that suits your preference (e.g., Dark Theme).

- Changing the theme updates the application's appearance, including colors, fonts, and interface elements, to reflect the selected design.

- The chosen theme is linked to your account and is automatically applied whenever you log in, ensuring a consistent experience across sessions.

![Setting Theme](media/user_guide_set_theme.gif)

## Language Options

Within the General Settings page, users can configure their preferred language using the options available in the Language Settings section.

- Under language settings, select your preferred language from the dropdown menu (e.g., `en-US`, `ko`, `he`, `ar`).

- Once selected, the interface will update immediately to reflect the chosen language. All labels, buttons, messages, and other text elements throughout the application will display in the selected language.

- Your selected language is saved to your profile and persists between sessions. Each time you log in, the application automatically applies your saved language preference.

- You may also select **"Default"** to always follow the system's default language as configured by the administrator. If the administrator changes the default language, your interface will update accordingly.

- If a translation is missing in your selected language, the application falls back in order to:
  1. The standard translation in the selected language
  2. The custom translation in the system default language
  3. The standard translation in the system default language

- **Right-to-Left (RTL) Support**: When a Right-to-Left language such as Arabic or Hebrew is selected, the entire interface layout automatically mirrors to support RTL reading. Text alignment, navigation menus, and all UI components adjust from right to left.

- **Note**: Only languages that have been configured by the administrator appear in the dropdown. If you need a language that is not listed, contact your administrator.

![Setting Language](media/user_guide_set_language.gif)

## Signature Settings

The signature settings section on the settings page allows users to define their personal email signature.

- This signature is automatically appended to the message body when composing an email.

- The signature is stored with the user’s profile and is automatically applied in future sessions, removing the need for repeated entry.

![Setting Language](media/user_guide_signature.png)

# Master Work Programs

The **Master Work Programs** page provides an overview of all LOTO work programs with customizable columns, inline editing, grid preferences, and work program locking.

## Overview

This page allows users to view, create, edit, and manage work programs. The data grid supports custom columns configured by administrators, along with user-specific preferences for column layout, filtering, and sorting.

## Features

### 1. Data Grid Customization

- **Column Filtering**: Click column headers to filter data
- **Column Reordering**: Drag column headers to rearrange
- **Column Resizing**: Drag column borders to resize
- **Sorting**: Click headers to sort ascending/descending
- **Header Filters**: Use the dropdown filters in column headers to filter by specific values

### 2. Custom Columns

The grid displays both system columns and custom columns configured by administrators:

- **System columns**: Work Program Name, SAP Number, Description, Created By, Last Modified, Last Modified By, Equipment, Status, Editing
- **Custom columns**: Additional columns defined in admin settings (e.g., Project Phase, Priority, Department)

Custom column values can be edited inline when the work program is not locked.

### 3. Inline Editing

- **Single-click** on a row to enter edit mode for custom column values
- **Double-click** to navigate to the work program details page
- Changes to custom column values are saved automatically

### 4. Grid Preferences

Your column layout, filters, and sorting preferences are saved automatically:

- Preferences persist across sessions
- Each user has their own saved preferences
- Click **Reset Formatting** to restore the default view

### 5. Work Program Locking

When another user is editing a work program, the row displays:

- Yellow background color
- Italic text styling
- Editor's name in the "Editing" column

When attempting to open a locked work program, you have three options:

- **Cancel**: Return to the list without opening
- **View Only**: Open the work program in read-only mode
- **Override**: Force release the lock and take over editing (use with caution)

### 6. Creating Work Programs

1. Click the **Start New Program** button
2. Fill in the work program details in the creation form
3. The new program appears in the grid

### 7. Deleting Work Programs

1. Click the **trash** icon on a row (only visible if the program is not locked)
2. Confirm deletion in the popup
3. The work program is soft-deleted (data preserved for recovery if needed)

# P&ID Multi-Use Blinds

The **P&ID Multi-Use Blinds** feature enables users to visualize, manage, and interact with sketch items (e.g., Blinds, Valves, Hoses) on active piping and instrumentation diagrams (P&IDs). Users can display both current and external work program items directly within the IntelliPID interface

## Adding List Items

Users within a work program can click the **Add list item** button to create a new entry for the selected list type.

- Items available include **Blinds, Hoses, Valves, Electrical Drives**, or other components, depending on the selected list type.
- When clicked, the application opens the **Sketching Tool** and lets you place catalogue items directly onto the 2D viewer. After placement, the tool switches to a details form where you can review and edit fields (a mix of user-entered and model-derived values). Submitting the form finalizes the new list item and adds it to the data grid for the active list type.

#### Steps to Add

1. **Open the Sketching Tool**
   Click **Add list item**. The **Sketching Tool** popup opens and displays the catalogue items available for placement on the active PID.

2. **Choose a Catalogue Item**
   In the popup, select the item and the specific catalogue entry you want to add.

3. **Place the Item on the 2D Viewer**
   Move your cursor over the 2D viewer window; a placement preview follows your cursor. Click to place the item at the desired location.

4. **Item is Created Automatically**
   After placement, the system auto-submits the item using the default field values. The list item is immediately created, linked to its placement on the PID, and added to the data grid for the active list type — no manual confirmation step is required.

   > **Note:** The **Type** column is a special system column that automatically displays the **Connected Item Name** (the name of the catalogue item placed on the P&ID). This value is set during placement and reflects which catalogue entry was selected.

5. **Re-select from the Table (Optional)**
   Click the item's row in the data grid to automatically load the 2D viewer window with that item selected and fitted to the viewport for easy inspection.

![Add list item](media/user_guide_add_list_item.gif)

### Automatic NoOnPID Generation

When a catalogue item or primitive is placed on the P&ID, the system automatically generates a **NoOnPID** identifier for the new list item. This identifier provides a sequential label that uniquely numbers each item within its list type for the current work program.

#### How It Works

- Each list type has a designated **prefix**:

  | List Type              | Prefix |
  | ---------------------- | ------ |
  | Blind List             | S      |
  | Valves List            | A      |
  | Electrical Drives List | E      |
  | Drains List            | D      |
  | Hose List              | H      |
  | Isolation Markup       | C      |

- The **number** following the prefix is determined by counting existing items of that list type in the work program and incrementing by one.
- For example, if a work program already has 3 items in the Valves List, the next placed valve will receive the NoOnPID **A4**.

### Editing List Items

Users can edit an existing list item directly from the data grid.

- Supported item types include **Blinds, Hoses, Valves, Electrical Drives**, and other components depending on the selected list type.
- Edits apply to the item's **placement** in the 2D viewer and its **connecting point(s)**. Changes are reflected immediately in the data grid.

#### Steps to Edit

1. **Enter Edit Mode**
   In the data grid, click the **Edit** (pencil) icon on the item row you want to modify. The P&ID sketch opens in the 2D viewer and **all rows that share the same P&ID** switch to edit state, allowing you to reposition any item on that sketch.

2. **Adjust Placement (2D Viewer)**
   Click and drag the item to the desired new location within the 2D viewer.

3. **Update Connecting Point(s)**
   Select the item's connector handle(s) and connect it to the new IntelliPID segment or target. This will update the appropriate value in the table.

4. **Rotate the Symbol (Optional)**
   While in edit mode, you can rotate the placed symbol for any row in 15° increments using two methods:
   - **R key (keyboard):** Press **R** while the data grid has focus. The symbol's rotation attribute is updated immediately. The visual change in the IntelliPID viewer will appear after you save.
   - **Rotate button (click):** Click the **Rotate** button in the row's action column (visible only while in edit mode). This triggers an immediate visual refresh in the IntelliPID viewer so you can see the rotated position before saving.

5. **Save Changes**
   Click the **Save** icon (visible on all rows sharing the same P&ID while in edit mode) on any of those rows to finalize your updates. All edited rows are saved together.

![Position Edit](media/user_guide_edit_list_item.gif)

#### Edit Values in Table

1. **Start Inline Edit**
   Double-click the cell in the data grid to activate its editor (e.g., text, dropdown, date).

2. **Enter New Value**
   Type or select the new value. Required fields must be valid before saving.

![Table Values Edit](media/user_guide_edit_list_item_value.gif)

### Deleting List Items

1. Click the **Trash** icon on the list item row
2. Confirm deletion

**Important**: Deleting a list item removes it from the database and the associated symbol from the P&ID (if placed).

### Nearby Work

Users working on a specific PID can click the **"Nearby Work"** button to automatically visualize all catalogue items that are associated with active work programs on that PID.

- Items displayed include **Blinds, Hoses, Valves, Electrical Drives**, or other components, depending on the selected list type.
- Once clicked, the application:
  - Fetches all sketch items from work programs linked to the opened PID or the same equipment tag.
  - Displays them directly within the active drawing view.
  - Enables contextual mapping between the sketch item and the IntelliPID segment selected.

![Nearby Work](media/user_guide_load_blinds.gif)

### Copying External Blinds

Blinds from **external work programs** (not currently active in the PID view) can be cloned into the current work program list.

#### Steps to Clone:

1. Click the **Planning** toggle to view external work program items (highlighted in yellow).
2. Click the **Copy** button next to the desired blind.
3. A **Clone Tool popup** will appear, prompting the user to place the blind into the drawing:

![Clone Tool](media/user_guide_clone_1.gif)

4. Upon placement:
   - The blind is rendered on the PID.
   - Metadata is fetched and pre-fills the Clone Tool popup.

![Clone Tool Details](media/user_guide_clone_2.gif)

### Multi-Use Blind Naming Convention

If a blind is reused across multiple work programs, it will reflect this in the naming convention shown in the data grid.

Format: `<BlindName>M<X>`

- Example: A blind named **S1** used in **two** other work programs will appear as **S1M2**.

### Interacting with P&ID Viewer

The P&ID viewer integrates with the list table:

**Select an item in the table**:

- Click a row in the list table
- The P&ID viewer automatically pans and zooms to that item's symbol
- The symbol is highlighted

**Select an item in the P&ID**:

- Click a symbol in the P&ID viewer
- The corresponding row in the table is highlighted

**Clear filters**:

- Click **"Clear Filter"** to reset the P&ID view

**Fit to view**:

- Select an item to automatically fit it to the viewport

## Steps Table

Each work program includes a **Steps Table** organized by phase tabs: **Preparation**, **De-commissioning**, **Repair**, and **Commissioning**. The steps table tracks the procedural steps required for each phase of the work program.

### Steps Table Columns

The steps table displays the following system columns plus any administrator-configured dynamic columns:

| Column              | Description                                                                                                                                                                  |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Order**           | Sequential number determining execution order (drag to reorder)                                                                                                              |
| **Step Title**      | Name/description of the step                                                                                                                                                 |
| **Item Type**       | Classification of the step, including the phase it belongs to                                                                                                                |
| **Dynamic Columns** | Additional columns configured by administrators for the current phase (e.g., Responsible Party, Action Owner). These vary per phase based on the metadata field definitions. |
| **Comment**         | Notes, instructions, or additional details                                                                                                                                   |
| **Interdependency** | Other steps (by Order number) that must be completed first                                                                                                                   |
| **Attachments**     | File attachments associated with the step                                                                                                                                    |

### Working with Steps

- **Add a step**: Click the **Add (+)** button while on the desired phase tab
- **Edit a step**: Click the **Edit (pencil)** icon or double-click a cell to edit inline
- **Delete a step**: Click the **Trash** icon on the step row
- **Reorder steps**: Drag using the handle at the left of each row
- **Set interdependencies**: Select prerequisite steps in the Interdependency column

### Importing Steps from Templates

When importing steps from a template (via the **Planning → Template** tab), all step data is copied including any values entered in dynamic columns. This means template steps can be pre-configured with dynamic field values that carry over to the work program.

Similarly, when cloning steps from another work program, dynamic field values are preserved in the copy.

## Summary Tab

The **Summary** tab provides an overview of the entire work program and generates comprehensive reports.

### Summary View

The Summary displays:

- **Work Program Header**: Name, SAP Number, Equipment details
- **Phase Summaries**: Count of steps per phase and completion status
- **List Summaries**: Count of items per list type
- **Metadata Preview**: Key fields configured to appear in summary reports (by administrators)

![Summary Tab](media/user_guide_loto_summary.png)

### Generating Reports

1. Navigate to the **Summary** tab
2. Review the summary information
3. Click **"Print all"**

The system generates a comprehensive PDF report and downloads it to your local machine. What the PDF contains depends on whether a print summary template has been configured by your administrator.

#### Standard Report (no template active)

When no template is active, the report includes:

- Work Program header information
- All steps from all phases with status
- All list items with their metadata
- P&ID sketches showing item placements

> **Note:** Boolean metadata fields (e.g., checkboxes) are displayed as a centered **"X"** for true values and left blank for false values in the standard PDF tables.

#### Template-Based Report (template active)

When an administrator has uploaded and activated a print summary template, the PDF is generated from that Excel-based layout instead. The template is pre-designed by your administrator to match your organisation's reporting format.

The template-based PDF:

- Uses the layout, colours, fonts, and section structure defined in the Excel template
- Fills `#FieldName` placeholders in the template with the work program's actual data (name, SAP number, equipment, dates, and any configured metadata fields)
- Lists P&ID drawing names in the section marked `#PIDSketches`
- Preserves any images embedded directly in the Excel template (logos, diagrams, icons) at their original position and size
- Appends captured P&ID sketch PDFs after the template pages, preserving all model layers
- Includes **interactive fields** you can fill in directly inside a PDF reader:
  - **Checkboxes** — cells marked as boolean in the template are rendered as tickable checkboxes
  - **Comment fields** — cells marked with `#Comment` become editable text fields you can type into
  - **Dropdown checklists** — dropdown metadata fields using a `#FieldName` token show all options with the current value pre-checked
  - **Inline dropdown values** — dropdown metadata fields using a `##FieldName` (double hash) token display only the selected value as plain text, without a checklist

> **Note:** If the work program is open in the model viewer, the system captures fresh P&ID sketches before generating the template PDF to ensure the connection to the model stays active. This may cause a brief delay before the download begins.

## View-Only Mode

If you open a work program that's locked by another user and choose **"View Only"**, the interface operates in read-only mode:

- All edit buttons are disabled
- You can view steps, lists, and details
- You cannot add, edit, or delete items
- The breadcrumb indicates "View Only" status

**Exiting View-Only Mode**: Close the work program and return later when the lock is released.

## Saving Work

**Auto-Save**: Changes to steps and list items are saved immediately when you press Enter or click elsewhere.

**UPVF Folder**: The work program maintains a UPVF folder containing:

- P&ID sketches with placed symbols
- File attachments
- Document history

**Folder Save**: The UPVF folder is automatically saved when you navigate away or close the work program.

**Best Practice**: Periodically verify your changes are saved, especially after placing multiple items on P&IDs.

## PowerBI ##
### Getting Information from PowerBI Report ###
- After you have created your Report, open "Share" -> "Link to this Visual" in PowerBI 

 ![Get Link from PowerBI](media/user_guide_powerbi_getlink.png)
- Copy the Link to a Text Editor, it will look something like this:
  -  https://app.powerbi.com/groups/< HERE IS WORKSPACE ID>/reports/< HERE  IS REPORT ID>/...
- Copy both values, they are required for Toolkit.

### Adding a new Report ###
- Click the + Button to add a new Report

![Add PowerBI Report](media/user_guide_powerbi_additem.png)
- Enter 'Report Name' (it's not necessary to use the same as in PowerBI, it's just for Toolkit)
- 'Report ID' which we got from the PowerBI URI (see *Getting Information from PowerBI Report*)
- 'Workspace ID' which we got from the PowerBI URI  (see *Getting Information from PowerBI Report*)
- 'Type report type' is used for grouping reports, if already a group exists, you can select it or provide any useful name for it.

### Troubleshooting ###
New report should now be available under the selected/created group. 
- If there aren't any entries, verify with your Admin, that:
  - Azure App Client ID, Client Secret and Tenant ID are set as Environment Variables for the Toolkit Server
  - Azure App Client has permissions in your PowerBI workspace
  - PowerBI Report ID and Workspace ID are correct

## Troubleshooting Common Issues

### "Invalid Selection" Error When Creating Work Program

- **Cause**: Selected objects have multiple different Tags or no Tag
- **Solution**: Ensure all selected objects share the same unique Tag value

### Cannot Complete a Step

- **Cause**: Step has interdependencies that are not yet complete
- **Solution**: Complete prerequisite steps first (check the Interdependency column)

### Symbol Not Appearing After Placement

- **Cause**: SVG file path is invalid or file is missing
- **Solution**: Contact your administrator to verify catalogue item configuration

### "Work Program Locked" Message

- **Cause**: Another user is currently editing the work program
- **Solution**: Wait for them to finish, use "View Only" mode, or coordinate to override

### Changes Not Saving

- **Cause**: Concurrent edit conflict or network issue
- **Solution**: Refresh the page and re-enter changes; contact support if persistent

### List Item Missing from P&ID

- **Cause**: Item was added without P&ID placement, or UPVF folder not saved
- **Solution**: Edit the item and place it on the P&ID; ensure folder is saved

## Additional Notes

- **Work Program Status**: Administrators may configure workflow status fields (e.g., Draft, In Progress, Complete, Closed)
- **SAP Integration**: If configured, work programs can link to SAP work orders for seamless ERP integration
- **Equipment Hierarchy**: Functional Location and Equipment Type help organize and filter work programs
- **Reusing Work Programs**: Mark work programs as "Reused" to indicate they've been applied to multiple scenarios

---

# Toolkit App for UDiTH — Toolkit Group And Role Mapping

# Toolkit Roles/Groups

## 1. User Removed from Group

**Action:**  
On Keycloak's side, the user is removed from a defined group.

**Result:**

- In the toolkit, the user remains part of the group until they log out and log back in. This is because the toolkit cannot react directly to changes made in Keycloak.
- A refresh of the user account session is required for changes to take effect.

---

## 2. Group Name Modified

**Action:**  
On Keycloak's side, the admin modifies a group name, for example:  
_Area Coordinator → Head Area Coordinator_

**Result:**

- From the toolkit's perspective, this is treated as a new group name being created.
- The old group name will still function as before.
- After the user restarts their session, they will see the new group name, resulting in a completely new relation. The user will also be removed from the old group.
- Data from the old relation will remain associated with the old name. An administrator must update all admin settings where the old group was used.

---

## 3. Keycloak Role Mapping

**Topic:**  
Explain how role mapping is implemented in the toolkit and the implications of modifications.

**Explanation:**

- Role Mapping is used to grant access to the toolkit.
- When navigating to different pages, access is restricted based on specific role mappings.
- Users without the required role mappings will be denied access to those pages.
- Administrators must ensure that role mappings are correctly assigned to users to avoid access issues.

---

## 4. Keycloak Groups

**Topic:**  
Describes how groups are utilized by the toolkit and the impact of modifications.

**Explanation:**

- Groups are the primary method for distinguishing user permissions in the toolkit.
- This includes:
  1. Whether the user has admin privileges.
  2. In Punchlist: Whether the user is permitted to approve workflow steps.
  3. In LOTO: Whether the user receives notifications as a maintenance or operations user.
  4. No group requirements are set up in Clashes or Redline.

---

## 5. Required Keycloak Groups

**Action:**  
Lists the groups required for the toolkit to function properly.

**Result:**

- **Maintenance and Operations:** Required by the current LOTO application (This is likely to change in February 2025).

## 6. Required Keycloak Role Mappings

**Action:**  
Lists the role mappings required for the toolkit to function properly.

**Result:**

- **Admin:** Grants global permissions in the toolkit, only "Admin" can view and change license key.

---

# Toolkit App for UDiTH — Toolkit Deployment Guide

# Deployment

## Prequisites

* Docker or Podman (to run the Toolkit Docker image)
* SQL Server (to host the database) (with SQL_Latin1_General_CP1_CI_AS Collation (see Database Section))
* Keycloak (for authentication and authorization)
* Optional: Microsoft Exchange (for email integration)
* Optional: Azure Blob Storage account (if using Azure for blob storage)
* Optional: Nginx or Apache (if using a reverse proxy)
* Optional: Cron (for scheduled tasks, e.g. email polling)
* Optional: PowerBI account (for PowerBI integration)

## Docker/Podman

Toolkit is a Docker container with a set of apps for UDiTH App such as Tickets/Punchlist, Clashes, Lockout/Tagout, and Redlining. You can pull the Toolkit using Docker:


    docker pull quay.io/caxperts/toolkit.net:latest or until toolkit 2026.3.x docker pull quay.io/caxperts/toolkit:latest

or (for the latest stable release)

    docker pull quay.io/caxperts/toolkit.net:stable or until toolkit 2026.3.x docker pull quay.io/caxperts/toolkit:stable

## Database

- The application requires a running SQL Server for its database. 
- The Database should be created with Collation SQL_Latin1_General_CP1_CI_AI.
- Migration scripts are provided that are used to populate the database schema.
- Credentials for a database owner account are required.
- Optional .NET image only starting with release 2026.4.0: Set `DATABASE_BACKUP_PATH` to a directory **on the SQL Server host**. The container then backs up the database before it applies pending migrations, with the filename `{database}_pre-migration_{yyyyMMdd-HHmmss}Z_{container}.bak`, time in UTC. The separator follows the path (`\` if the path contains one, else `/`). Restoring the DB after a bad migration:
1. Stop the stack.
2. On SQL Server: `RESTORE DATABASE [<db>] FROM DISK = N'<file>' WITH REPLACE;`
3. Start the **previous image tag**. The restored schema matches the old code. The new image
   applies the same migrations again.

![Collation setup](media/admin_guide_database_setup.png)

## Local or Azure blob storage

Admin can specify an environment variables that will change how the application saves blobs. Those variables are:

- STORAGE_TYPE - accepts only 2 values, "local" or "azure".
- STORAGE_PATH - when "local" type used, defines an absolute path to storage on the hardware the server is run, e.g. "/home/user/localStorage".
- AZURE_STORAGE_CONNECTION_STRING - connection string given by Azure Blob Storage.
- AZURE_STORAGE_CONNECTION_DOMAIN - the domain defined by Azure Blob Storage.

## Reverse Proxy Setup

Toolkit does not do TLS itself. The container listens with plain HTTP on port **3000**. Put a reverse proxy (Nginx Proxy Manager, Nginx or Apache) in front of the container. The reverse proxy does these tasks:

- It terminates HTTPS. Keycloak login works only over HTTPS (see [Important Notes](#important-notes)).
- It supplies HTTP/2 to the browser.
- It forwards all requests to the Toolkit container (single instance) or to the Toolkit Nginx container (multi-tenancy, see [Multi-tenancy](#multi-tenancy-path-based-multi-tenancy)).

### Required settings

| Setting | Value | Reason |
|---|---|---|
| Request header size | at least `4 x 32k` (Nginx: `large_client_header_buffers`) | Keycloak tokens and cookies make large request headers. If the value is too small, the proxy returns `400 Bad Request` and the user cannot log in. |
| Response header buffers | `proxy_buffers 16 16k`, `proxy_buffer_size 32k`, `proxy_busy_buffers_size 64k` | Large response headers (tokens, cookies). If the value is too small, the proxy returns `502 Bad Gateway`. |
| Maximum request body | `256m` (Nginx: `client_max_body_size`) | Uploads of attachments (max. 50 MB), plugins (max. 100 MB) and UPVF files (max. 250 MB). If the value is too small, the proxy returns `413 Request Entity Too Large`. |
| Timeouts | `300s` | Report generation and imports can take some minutes. If the value is too small, the proxy returns `504 Gateway Timeout`. |
| Path | Forward the path unchanged | Do not remove or add a path prefix and do not add trailing-slash redirects. Toolkit handles its subpath itself (`BASE_PATH`). |

### HTTP/2 support

The browser connects to the reverse proxy with HTTP/2. The reverse proxy connects to the Toolkit container with HTTP/1.1. You do not change the Toolkit container to enable HTTP/2.

HTTP/2 loads the Toolkit web UI faster, because the browser loads many JavaScript files in parallel over one connection.

Browsers use HTTP/2 only over HTTPS. Thus you must enable TLS first.

| Reverse proxy | Setting |
|---|---|
| Nginx Proxy Manager | Tab **SSL** > **HTTP/2 Support** = on |
| Nginx 1.25.1 and later | `listen 443 ssl;` and `http2 on;` |
| Nginx before 1.25.1 | `listen 443 ssl http2;` |
| Apache 2.4.17 and later | Load `mod_http2`, then set `Protocols h2 http/1.1`. Use the `event` MPM: HTTP/2 does not work with `prefork`. |

To make sure that HTTP/2 operates, run:

```bash
curl -sI --http2 https://toolkit.example.com/
```

The first line of the output must be `HTTP/2 200`. On Windows PowerShell, type `curl.exe` instead of `curl`.

You can also open the browser developer tools (F12) > **Network** and show the **Protocol** column. The value must be `h2`.

### Example: Nginx Proxy Manager

1. Open **Hosts** > **Proxy Hosts** > **Add Proxy Host**.
2. On the tab **Details**, set these values:
   - **Domain Names**: `toolkit.example.com`
   - **Scheme**: `http`
   - **Forward Hostname / IP**: the service name of the Toolkit container, e.g. `toolkit`. For multi-tenancy, use the service name of the Toolkit Nginx container, e.g. `toolkit_entrypoint`.
   - **Forward Port**: `3000`. For multi-tenancy, use `80`.
   - **Block Common Exploits**: on
   - **Websockets Support**: not necessary

   ![Nginx Proxy Manager: tab Details](media/deployment_guide_npm_details.png)

3. On the tab **SSL**, set these values:
   - **SSL Certificate**: your certificate, or **Request a new SSL Certificate** (Let's Encrypt)
   - **Force SSL**: on
   - **HTTP/2 Support**: on
   - **HSTS Enabled**: optional

   ![Nginx Proxy Manager: tab SSL](media/deployment_guide_npm_ssl.png)

4. On the tab **Advanced**, put this text in **Custom Nginx Configuration**:

   ```nginx
   large_client_header_buffers 4 32k;
   proxy_buffers 16 16k;
   proxy_buffer_size 32k;
   proxy_busy_buffers_size 64k;
   client_max_body_size 256m;
   proxy_connect_timeout 300s;
   proxy_send_timeout 300s;
   proxy_read_timeout 300s;
   ```

   ![Nginx Proxy Manager: tab Advanced](media/deployment_guide_npm_advanced.png)

5. Click **Save**.

Nginx Proxy Manager must be on the same Docker network as the Toolkit container. Otherwise it cannot find the service name.

Do not add **Custom Locations** for the instance paths of a multi-tenant setup. Forward all requests to the Toolkit Nginx container. Custom locations can remove the trailing slash and cause the error "too many redirects".

### Example: Nginx

```nginx
# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name toolkit.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    http2 on;                      # Nginx before 1.25.1: "listen 443 ssl http2;"
    server_name toolkit.example.com;

    ssl_certificate     /etc/nginx/certs/fullchain.pem;
    ssl_certificate_key /etc/nginx/certs/privkey.pem;

    large_client_header_buffers 4 32k;
    client_max_body_size 256m;

    proxy_connect_timeout 300s;
    proxy_send_timeout    300s;
    proxy_read_timeout    300s;

    location / {
        # Single instance: the Toolkit container on port 3000.
        # Multi-tenancy: the Toolkit Nginx container on port 80, e.g. http://toolkit_entrypoint:80;
        proxy_pass http://toolkit:3000;
        proxy_http_version 1.1;

        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host  $host;

        proxy_buffers 16 16k;
        proxy_buffer_size 32k;
        proxy_busy_buffers_size 64k;
    }
}
```

Note: `proxy_pass` has no path after the port. Thus Nginx forwards the request path unchanged.

### Example: Apache

Required modules: `mod_ssl`, `mod_http2`, `mod_proxy`, `mod_proxy_http`, `mod_headers`, `mod_rewrite`.

```apache
<VirtualHost *:80>
    ServerName toolkit.example.com
    RewriteEngine On
    RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</VirtualHost>

<VirtualHost *:443>
    ServerName toolkit.example.com
    Protocols h2 http/1.1

    SSLEngine on
    SSLCertificateFile    /etc/ssl/certs/fullchain.pem
    SSLCertificateKeyFile /etc/ssl/private/privkey.pem

    # Large request headers (Keycloak) and 256 MB uploads
    LimitRequestFieldSize 32768
    LimitRequestBody      268435456
    ProxyTimeout          300

    ProxyPreserveHost On
    RequestHeader set X-Forwarded-Proto "https"

    # Single instance: port 3000. Multi-tenancy: the Toolkit Nginx container on port 80.
    ProxyPass        / http://toolkit:3000/
    ProxyPassReverse / http://toolkit:3000/
</VirtualHost>
```

### Troubleshooting

| Error | Cause | Solution |
|---|---|---|
| `400 Bad Request`, "Request Header Or Cookie Too Large" | The request header buffer is too small. | Increase `large_client_header_buffers` (Nginx) or `LimitRequestFieldSize` (Apache). |
| `502 Bad Gateway` after login | The response header buffer is too small, or the proxy cannot find the container. | Increase `proxy_buffer_size`. Make sure that the proxy and the container are on the same Docker network. |
| `413 Request Entity Too Large` | The upload is larger than the body limit. | Increase `client_max_body_size` (Nginx) or `LimitRequestBody` (Apache). |
| `504 Gateway Timeout` | A report or an import takes longer than the timeout. | Increase the timeouts to `300s`. |
| "Too many redirects" | A proxy removes the trailing slash, or a legacy (Next.js) container is behind the .NET Toolkit Nginx. | Forward the path unchanged. Use the Toolkit Nginx image that matches the Toolkit image. |
| Protocol in browser is `http/1.1` | HTTP/2 is off, or the connection does not use HTTPS. | Enable HTTP/2 on the reverse proxy (see [HTTP/2 support](#http2-support)). |

## Keycloak Clients and Scopes Setup

To integrate with Keycloak, you need to configure a custom client and associated scopes. Below are the step-by-step instructions for setting up the client and its scopes.

### Client scope configuration
Create a client scope named "groups" with the following settings:

#### Scope Settings

- **Name:** groups
- **Description:** "description"
- **Type:** None
- **Protocol:** OpenID Connect
- **Display on Consent Screen:** On
- **Consent screen text:** if required
- **Include in Token Scope:** On_
- **Display Order:** Leave empty

#### Mappers

1. Add a predefined mapper.
2. Select the "groups" mapper to include group information in the token.

#### Scope Details

- Leave empty by default unless specific mappings are required.

### Client Configuration

Create a custom client in Keycloak with the following settings to suit your application’s needs.

#### General Settings

- **Client Type:**
  - Leave as default or select "OpenID Connect" if prompted.

#### Capability Configuration

- **Client Authentication:**

  - Off (If confidential access required mark it True)

- **Authorization:**

  - Enable or disable based on the client’s specific requirements.

- **Authentication Flow:**
  - **Standard Flow:** Enabled
  - **Direct Access Grants:** Enabled
  - Enable additional flows as needed based on your use case.

#### Login Settings

- **Root URL:**
  - Leave empty.
- **Home URL:**
  - Leave empty.
- **Valid Redirect URIs:**
  - https://your.domain.com/* (Adjust to match your domain and paths).
- **Valid Post-Logout Redirect URIs:**
  - - (Use defaults or specify custom URIs as required).
- **Web Origins:**
  - - (Use defaults or specify allowed origins explicitly).### Client Scopes Configuration

### User groups
As a minimal setup, a user group named "Admin" exactly in that spelling is required.
A typical user groups setup would look like this: 
![Example of user groups configuration in Keycloak](media/deployment_guide_typical_user_groups.png)



## Email Configuration

The application uses two separate email accounts for different purposes:

### Support Email (Interactions)

The **SUPPORT_EMAIL** account is used for the ticket/interaction system. It serves as the single mailbox for:

- **Inbox scanning**: A cron job polls this mailbox every minute for new incoming emails and automatically creates tickets from them.
- **Sending replies**: When a user replies to a ticket from the Toolkit UI, the reply is sent from this email address.
- **Conversation threading**: The Toolkit UI fetches email conversation chains from this mailbox.

The relevant environment variables are: `AZURE_AD_TENANT_ID`, `SUPPORT_EMAIL`, `SUPPORT_CLIENT_ID`, and `SUPPORT_CLIENT_SECRET`.

To enable inbox scanning and automatic ticket creation, set `ENABLE_CRON_TICKET_CREATION=true`. When set to `false` or left unset, the cron job for ticket creation will not run, and incoming emails will not be processed into tickets.

### Workflow Email

The **WORKFLOW_EMAIL** account is used exclusively for outbound workflow notification emails. When a workflow step is accepted and advances to the next step, notification emails are sent to the responsible users from this address. This mailbox is never scanned for incoming emails and is not used for ticket creation.

The relevant environment variables are: `AZURE_AD_TENANT_ID`, `WORKFLOW_EMAIL`, `WORKFLOW_CLIENT_ID`, and `WORKFLOW_CLIENT_SECRET`.

### Azure AD App Registration

Both email accounts require an Azure AD app registration with Microsoft Graph `Mail.Send` and `Mail.Read` permissions (application-level) for their respective mailboxes. The `AZURE_AD_TENANT_ID` is shared between both accounts.

For detailed support in setting up the Azure AD app registrations, please contact us directly. If the toolkit should also be able to send notifications via the Keycloak service client, a Keycloak client is required (i.e. SERVICE_CLIENT_ID and SERVICE_CLIENT_SECRET as described in the following section).


## Setup for Keycloak role sync for with Service Client
To synchronize roles from Keycloak with Toolkit (and also to send notifcations by email), a service client is required. This service client is triggered with the Keycloak group sync cron job. The schedule of this cron job is defined in KC_GROUP_SYNC_CRON.

### Creating the service client in Keycloak

1. Navigate to tab 'Clients'

2. Create a new client:

3. General Settings:

   - Client Type: OpenID Connect
   - Client ID: provide client id name that you want to use
   - Name: Optional
   - Description: Optional

4. Capability config:
   - Client authentication: ON
   - Authorization: OFF
   - Only Service accounts roles is checked

### Adding Groups scope to service client

1. Navigate to tab 'Clients'

2. Choose your newly created client
Here named "serviceclient": 
![Keycloak Panel for service client](media/deployment-keycloak-service-client.png)

3. Navigate to tab 'Client Scopes'

4. Add client scope

5. Find in scopes the following:

   - Name: 'groups'
   - Protocol: 'OpenID Connect'

6. Add groups scope as 'default'

### Assigning account roles to the Keycloak service client and groups to corresponding service client user

1. Navigate to the newly created client id.

2. Navigate to 'Service accounts roles' tab.

3. Assign the service account roles as specified in the picture below: 

![Keycloak service client privileges](media/keycloak_hidden_user.png)

4. Make sure that user account corresponding to the service client (in this example called 'service-account-serviceclient') is member of the Admin group via the link hightlighted in the picture above. 

### Keycloak service client in environment variables
SERVICE_CLIENT_ID is your service client name, SERVICE_CLIENT_SECRET can taken from the "Credential" tab, labelled Client secret. 

## Comprehensive Environment Variables List

(Variables marked as **bold** are the minimum variables required to start Toolkit)

//Database connection until 2026.3.x
**DATABASE_URL** = <sql_provider>:<port>;database=<database>;user=<user>;password=<password>trustServerCertificate=true
//Database connection starting 2026.4.0 
**ConnectionStrings__DefaultConnection**=Server=<SERVERNAME>,<SERVERPORT>;Database=<DATABASENAME>;User Id=<DATABASEUSERNAME>;Password=<PASSWORD>;Encrypt=false;TrustServerCertificate=true;MultipleActiveResultSets=true

**KEYCLOAK_BASE_URL** = <url> e.g. https://cax.authudith.com

**KEYCLOAK_ID** = <id> e.g. connector

**KEYCLOAK_ISSUER** = <issuer> e.g. https://cax.authudith.com/realms/apps-demo

**KEYCLOAK_REALM** = <realm> e.g. apps-demo

**KEYCLOAK_REDIRECT_URL** = <appcontrol_url> e.g. https://udithappcontrol.com

**SERVICE_CLIENT_ID**="<id>" // Keycloak client id created specifically for cron, required for group synchronization

**SERVICE_CLIENT_SECRET**="<secret>" // Existing keycloak client secret.

**NEXTAUTH_URL** = <appcontrol_url> e.g. https://udithappcontrol.com

**NEXTAUTH_SECRET** = "any_string_that_represents_secret" // required by nextauth to secure authentication via application

REDIRECT_WEBSERVICES_URL="<upv_web_based_redirect_url>", required for usage in browser-based viewing

AZURE_AD_TENANT_ID=""000000-0000-0000-0000-000000000"

SUPPORT_EMAIL="<email>@<domain>" // Email used for interaction inbox scanning, ticket creation, and sending replies, required for inbound emails

SUPPORT_CLIENT_ID="000000-00-0000-0000-000000000" // Azure AD app client ID for the support email, required for inbound emails

SUPPORT_CLIENT_SECRET="your_secret" // Azure AD app client secret for the support email, required for inbound emails

WORKFLOW_EMAIL="<email>@<domain>" // Email used for outbound workflow notification emails only (never scanned), required for outbound emails

WORKFLOW_CLIENT_ID="000000-00-0000-0000-000000000" // Azure AD app client ID for the workflow email, required for outbound emails

WORKFLOW_CLIENT_SECRET="your_secret" // Azure AD app client secret for the workflow email, required for outbound emails

KC_GROUP_SYNC_CRON=0 3 \* \* \* // this variable is used to run the user roles sync with keyccloak, default is set to every day at 3 am  default.

ENABLE_CRON=<boolean> // 'true' if you want cron job to be running (default), otherwise leave it empty or 'false'

ENABLE_CRON_TICKET_CREATION=<boolean> // 'true' to enable automatic ticket creation from incoming emails, otherwise 'false'

DEVICE_ID = <device_id> e.g. AppControls-udithappcontrol.com, Displayed to UDiTH when authenticating

LOGIN_HEADER = <header_displayed_on_login> e.g. CAXperts

SIGNATURE_REPORT_THRESHOLD = <number> e.g. 1536, represents image size threshold in bytes to filter low quality signature like "dot"

DATABASE_BACKUP_PATH = <pathOnSQLServer> //  e.g. '/var/opt/mssql/data/' , supported starting version 2026.4.0 and later, location of pre-migration database backups

STORAGE_PATH= <absolute_path_to_storage> e.g."/app/storage". If using docker volumes make sure that this path matches the definition there, e.g. use "/app/storage" if it says "toolkit_volume:/app/storage" in the docker stack definition.

STORAGE_TYPE= <storage_type> takes value of "local" || "azure"

AZURE_STORAGE_CONNECTION_STRING = <string> // connection string given by Azure Blob Storage.

AZURE_STORAGE_CONNECTION_DOMAIN = <domain> e.g. caxperts.blob.core.windows.net // the domain defined by Azure Blob Storage.

NEXT_PUBLIC_TICKET_PREFIX="PI-"

PORT=3000

HOST=0.0.0.0

POWERBI_LOGO = <URL_to_logo> it will be displayed on powerbi page

POWERBI_TENANT_ID=<uuid>

POWERBI_CLIENT_ID=<uuid>

POWERBI_SECRET=<secret>


## Browser Authentication in UDiTH Mode

By default, when the Toolkit runs inside the UDiTH application, it uses UDiTH's authentication context for single sign-on. However, this can be incompatible with reverse proxies like Azure EasyAuth that handle authentication at the HTTP level.

### Environment Variable

To enable browser-based authentication even when running inside UPV, set:

```bash
USE_BROWSER_AUTH_IN_UPV=true
```

### Behavior

- **Default (USE_BROWSER_AUTH_IN_UPV=false or unset)**: UDiTH App uses in-app authentication context
- **Enabled (USE_BROWSER_AUTH_IN_UPV=true)**: UDiTH App uses browser authentication flow (same as web browser)

### Use Cases

This flag is particularly useful for:

- **Azure EasyAuth Integration**: When placing Azure App Service EasyAuth in front of the application
- **Corporate Proxy Authentication**: When authentication is handled by enterprise reverse proxies
- **Centralized Identity Management**: When all authentication should flow through a single identity provider endpoint

### Configuration Example

For Azure App Service with EasyAuth:

```bash
# Enable browser auth in UDiTH App
USE_BROWSER_AUTH_IN_UPV=true

# Standard Keycloak configuration (same as browser deployment)
KEYCLOAK_ID=your-client-id
KEYCLOAK_SECRET=your-client-secret
KEYCLOAK_ISSUER=https://your-keycloak-server/realms/your-realm
```

### Testing

1. Deploy with `USE_BROWSER_AUTH_IN_UPV=true`
2. Open the application in UDiTH App
3. Verify that clicking "Sign In" redirects to browser authentication instead of opening UDiTH App authentication dialog
4. Confirm that authentication works through your proxy/EasyAuth setup

## Breaking Change

1. When using STORAGE_TYPE=local with mapped container volume:
   - Please make sure that the mapped volume is owned by user 1000. Otherwise, on blob saving 'permission denied' will be raised.


## Example docker-compose.yml (Full Setup)

Below is an example docker-compose file for a single Toolkit instance (.NET image) together with SQL Server and Keycloak. It contains only the variables that are necessary to start Toolkit. Add optional variables from the [Comprehensive Environment Variables List](#comprehensive-environment-variables-list) as required.

```yaml
services:
  toolkit:
    image: quay.io/caxperts/toolkit.net:latest
    ports:
      - 3000:3000
    environment:
      ConnectionStrings__DefaultConnection: Server=db,1433;Database=toolkit_db;User Id=sa;Password=changeme_Db1;Encrypt=false;TrustServerCertificate=true;MultipleActiveResultSets=true
      KEYCLOAK_ID: connector
      KEYCLOAK_ISSUER: https://keycloakdomain.com/realms/toolkit_realm
      KEYCLOAK_REALM: toolkit_realm
      SERVICE_CLIENT_ID: serviceclient
      SERVICE_CLIENT_SECRET: demoSecret2
      STORAGE_PATH: /app/storage # must be ABSOLUTE and match the volume mount below
      # Plugins
      Plugins__Path: /app/plugins # must be ABSOLUTE and match the volume mount below
      Plugins__AutoMigrate: "true" # applies plugin database migrations on startup (needs CREATE SCHEMA permission)
      App__AllowRestart: "true" # lets an Admin restart Toolkit to activate a plugin (needs restart: unless-stopped)
    volumes:
      - toolkit-storage:/app/storage
      - toolkit-plugins:/app/plugins # installed plugins, keep separate from storage
    depends_on:
      db:
        condition: service_healthy
    restart: unless-stopped

  db:
    image: mcr.microsoft.com/mssql/server:2025-latest
    environment:
      ACCEPT_EULA: "Y"
      MSSQL_SA_PASSWORD: changeme_Db1
      MSSQL_COLLATION: SQL_Latin1_General_CP1_CI_AS
    volumes:
      - sqldata:/var/opt/mssql
    healthcheck:
      test: /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "$$MSSQL_SA_PASSWORD" -C -Q "SELECT 1" || exit 1
      interval: 10s
      retries: 5
    restart: unless-stopped

  keycloak:
    image: quay.io/keycloak/keycloak:26.5.2
    command: start-dev
    ports:
      - 8080:8080
    environment:
      KC_BOOTSTRAP_ADMIN_USERNAME: admin
      KC_BOOTSTRAP_ADMIN_PASSWORD: changeme
      KC_HOSTNAME: https://keycloakdomain.com # public HTTPS URL of the reverse proxy in front of Keycloak
      KC_PROXY_HEADERS: xforwarded
    restart: unless-stopped

volumes:
  toolkit-storage:
  toolkit-plugins:
  sqldata:
```

### Important Notes

1. Keycloak must be reachable via **HTTPS** at the same URL (`KEYCLOAK_ISSUER`) from the browser and from the Toolkit container. Toolkit does not start if the Keycloak URL uses plain HTTP, except for `localhost`. Use a reverse proxy with TLS in front of Keycloak (and Toolkit).
2. Configure the realm, the `connector` client, the "groups" scope, the "Admin" group and the service client as described in [Keycloak Clients and Scopes Setup](#keycloak-clients-and-scopes-setup) and [Setup for Keycloak role sync for with Service Client](#setup-for-keycloak-role-sync-for-with-service-client).
3. Toolkit creates the database and applies the migrations on startup.
4. `start-dev` runs Keycloak without its own database and is not intended for production. For production, run Keycloak with an external database as described in the Keycloak documentation.
5. Plugins are installed by an Admin in Toolkit or put as a folder on the `toolkit-plugins` volume. See [06 Toolkit Plugins.md](06%20Toolkit%20Plugins.md). If the database user cannot create schemas, set `Plugins__AutoMigrate: "false"`.


## Multi-tenancy (Path-Based Multi-tenancy)

### Overview

This feature enables deployment of multiple Toolkit instances on a single domain or subdomain, each accessible via a unique subpath (e.g., `/instance1`, `/instance2`).

**Example URLs:**
- https://mydomain.com/instance1
- https://mydomain.com/instance2
- https://sub.mydomain.com/instance1
- https://sub.mydomain.com/instance2

Each instance can have its own Microsoft SQL Server database and Keycloak Identity Provider. It is strongly recommended to use a separate database for each instance to fully benefit from multi-tenancy.

### Use Cases

**1. Segregated Access Control**

Toolkit does not natively support multi-tenancy. Deploying multiple instances allows you to restrict access to data (e.g., punchlist items) by user group. For example, users in group A only see items relevant to category A, while group B sees category B.

**2. Simplified Deployment**

With multi-tenancy, IT admins only need to acquire a single domain and SSH key. Previously, each new instance required a separate domain setup, which was time-consuming and error-prone. Now, one domain can host multiple instances, streamlining the process.

### Requirements

- Recent Toolkit Docker image (e.g., `toolkit.net:stable`).
- Nginx reverse proxy Docker image (e.g., `toolkit.net:entrypoint`).
- Separate SQL Server databases for each instance (recommended).
- At least one Keycloak Identity Provider (shared sessions possible).
- Modified docker-compose file for multi-instance deployment.
- Adjusted environment variables for each instance in the docker-compose file.

### Example docker-compose.yml

Below is an example docker-compose file deploying one Nginx reverse proxy and two Toolkit instances. The Nginx container auto-generates routing based on the `INSTANCES` environment variable. SQL Server and Keycloak are assumed to be running externally.

```yaml
version: '3.8'

services:
  toolkit_entrypoint:
    image: quay.io/caxperts/toolkit.net:entrypoint
    #ports: <- only set if no reverse proxy e.g. nginx proxy manager is used
    #  - 80:80 
    environment:
      INSTANCES: "instance1,instance2"
    depends_on:
      - instance1
      - instance2
    restart: unless-stopped
    # volumes:
    #   - ./index.html:/usr/share/nginx/html/index.html:ro
    networks:
      - toolkit_network
      - external

  instance1:
    image: quay.io/caxperts/toolkit.net:stable
    container_name: toolkit-instance1
    environment:
      BASE_PATH: "/instance1"
      INSTANCE_NAME: "instance1"
      NEXT_PUBLIC_BASE_PATH: "/instance1"
      # Until version 2026.3.x
      #DATABASE_URL: sqlserver://sqlServerEngine:1433;database=toolkit_db1;user=toolkit_user_db1;password=changeme;trustServerCertificate=true;MultipleActiveResultSets=true
      ConnectionStrings__DefaultConnection: Server=sqlServerEngine,1433;Database=toolkit_db1;User Id=toolkit_user_db1;Password=changeme;Encrypt=false;TrustServerCertificate=true;MultipleActiveResultSets=true
      KEYCLOAK_BASE_URL: https://keycloakdomain.com
      KEYCLOAK_ID: connector
      KEYCLOAK_ISSUER: https://keycloakdomain.com/realms/toolkit_realm
      KEYCLOAK_REALM: toolkit_realm
      NEXTAUTH_URL: https://toolkitBaseUrl.com/instance1/api/auth
      NEXTAUTH_SECRET: demoSecret1
      STORAGE_PATH: /app/storage # must be ABSOLUTE and match the volume mount below
      STORAGE_TYPE: local
      NODE_ENV: production
      BODY_SIZE_LIMIT: 200mb
      SERVICE_CLIENT_ID: serviceclient
      SERVICE_CLIENT_SECRET: demoSecret2
      SERVICE_ACCOUNT_SECRET: demoSecret2
      DEVICE_ID: Toolkit_instance1
      KEYCLOAK_REDIRECT_URL: https://toolkitBaseUrl.com/instance1
      SUPPORT_CLIENT_ID: 00000000-0000-0000-0000-000000000000
      SUPPORT_CLIENT_SECRET: demoSecret3
      SUPPORT_EMAIL: support@company-name.com
      AZURE_AD_TENANT_ID: 00000000-0000-0000-0000-000000000001
      KC_GROUP_SYNC_CRON: "*/10 * * * *"
      # Email notification
      WORKFLOW_EMAIL: noreply@company-name.com
      WORKFLOW_CLIENT_ID: 00000000-0000-0000-0000-00000000002
      WORKFLOW_CLIENT_SECRET: demoSecret4
    volumes:
      - instance1-storage:/app/storage
    restart: unless-stopped
    networks:
      - toolkit_network
      - external

  instance2:
    image: quay.io/caxperts/toolkit.net:stable
    container_name: toolkit-instance2
    environment:
      BASE_PATH: "/instance2"
      INSTANCE_NAME: "instance2"
      NEXT_PUBLIC_BASE_PATH: "/instance2"
      # legacy unter 2026.3.x
      #DATABASE_URL: sqlserver://sqlServerEngine:1433;database=toolkit_db2;user=toolkit_user_db2;password=changeme;trustServerCertificate=true;MultipleActiveResultSets=true
      ConnectionStrings__DefaultConnection: Server=sqlServerEngine,1433;Database=toolkit_db2;User Id=toolkit_user_db2;Password=changeme;Encrypt=false;TrustServerCertificate=true;MultipleActiveResultSets=true
      KEYCLOAK_BASE_URL: https://keycloakdomain.com
      KEYCLOAK_ID: connector
      KEYCLOAK_ISSUER: https://keycloakdomain.com/realms/toolkit_realm
      KEYCLOAK_REALM: toolkit_realm
      NEXTAUTH_URL: https://toolkitBaseUrl.com/instance2/api/auth
      NEXTAUTH_SECRET: demoSecret1
      STORAGE_PATH: /app/storage # must be ABSOLUTE and match the volume mount below
      STORAGE_TYPE: local
      NODE_ENV: production
      BODY_SIZE_LIMIT: 200mb
      SERVICE_CLIENT_ID: serviceclient
      SERVICE_CLIENT_SECRET: demoSecret2
      SERVICE_ACCOUNT_SECRET: demoSecret2
      DEVICE_ID: Toolkit_instance2
      KEYCLOAK_REDIRECT_URL: https://toolkitBaseUrl.com/instance2
      SUPPORT_CLIENT_ID: 00000000-0000-0000-0000-000000000000
      SUPPORT_CLIENT_SECRET: demoSecret3
      SUPPORT_EMAIL: support@company-name.com
      AZURE_AD_TENANT_ID: 00000000-0000-0000-0000-000000000001
      KC_GROUP_SYNC_CRON: "*/10 * * * *"
      # Email notification
      WORKFLOW_EMAIL: noreply@company-name.com
      WORKFLOW_CLIENT_ID: 00000000-0000-0000-0000-00000000002
      WORKFLOW_CLIENT_SECRET: demoSecret4
    volumes:
      - instance2-storage:/app/storage
    restart: unless-stopped
    networks:
      - toolkit_network

networks: # should be created seperately 
  toolkit_network:
    external: true # set to false if the network should be auto-created with docker-compose up (not recommended)
    name: "toolkit_network"
  external:
    external: true # set to false if the network should be auto-created with docker-compose up (not recommended)
    name: "external"

volumes:
  instance1-storage:
  instance2-storage:
```

### Important Notes

1. Toolkit instance service names (for example instance2 as shown in above docker-compose file) must be unique for the specified docker-network (here; toolkit_network and external) and **MUST** correlate to the values specified in the **INSTANCES** variable in the nginx service. So if you set INSTANCES=app1,app2,app3,... then the service names of the toolkit instances in the docker-compose file must also be app1:,app2:,app3:,... Apart from that rules you can set any name like foo or bar for the individual instances.
2. The Nginx container, Toolkit instances, and SQL Server container must be on the same Docker network (e.g., `toolkit_network`) for proper domain resolution.
3. If using a reverse proxy (e.g., Nginx Proxy Manager), ensure the Nginx container is on the same network for accessibility.
4. All `KEYCLOAK_REDIRECT_URL` values must be set as "Valid redirect URIs" and "Valid post logout redirect URIs" in your Keycloak realm for the client specified in `KEYCLOAK_ID`.
5. Each Toolkit instance requires a valid license and consumes one session per user. A user logged into two instances uses two sessions.

### Multi-tenancy on the .NET stack

Toolkit images from the .NET release line (one container serving the API, the web UI and the
background worker) support the same path-based multi-tenancy, configured the same way. The
differences an administrator needs to know:

**1. Use the matching Nginx image.** Multi-tenancy on the .NET stack needs the Nginx image
built for it, in the same repository as the Toolkit image (for
example `quay.io/caxperts/toolkit.net:entrypoint-latest`). Deploy the two with matching
tags.

**2. The instance's subpath.** `BASE_PATH: "/instance1"` continues to work unchanged, so an
existing multi-instance docker-compose file needs no edit for the subpath. The variable the
new image reads natively is `App__BasePath`; `NEXT_PUBLIC_BASE_PATH` is no longer needed
(nothing is baked into the UI at build time any more) but is accepted as a fallback.

**3. The container port is unchanged.** The .NET container listens on **3000**, the same port
the Next.js image used, so a hand-written proxy configuration or a published port mapping keeps
working as it is.

**Running the new stack alongside the old one.** Docker publishes every service name as a
hostname on each network the container joins, so two stacks that both contain services named
`instance1`, `instance2` and the same Nginx service leave those names ambiguous — and the new
proxy will happily serve the *old* application under the new URL. Give the new stack unique
service names, and keep the public URLs by writing each `INSTANCES` entry as
`subpath:servicename`:

```yaml
  toolkit_net_entrypoint:            # unique Nginx service name
    environment:
      INSTANCES: "instance1:net_instance1,instance2:net_instance2"
  net_instance1:                     # unique instance service name, still served at /instance1
    environment:
      App__BasePath: /instance1
```

Point Nginx Proxy Manager's **Forward Hostname / IP** at the new Nginx service name. Once the
old stack is retired the plain `INSTANCES: "instance1,instance2"` form can be restored.

**4. One container per instance.** The background worker is bundled, so the separate cron
service disappears. Leave `RUN_WORKER: "true"` on **every** instance, including when they share
one Keycloak realm: the worker reads the shared realm but writes the users and roles into *that
instance's own* database, so an instance without a worker never learns who its users are and
group-based access there stays empty. Several instances against one realm means duplicated
reads, not conflicting writes.

The Keycloak sync schedule is now an interval in minutes rather than a cron expression. A legacy
`KC_GROUP_SYNC_CRON: "*/10 * * * *"` is understood and carried over as 10 minutes; a fixed time
such as `0 3 * * *` cannot be expressed as an interval, so set
`Worker__KeycloakSyncIntervalMinutes` yourself in that case (the container logs a warning and
otherwise syncs every 30 minutes).


Each instance keeps its own database, its own storage volume and its own
`KEYCLOAK_REDIRECT_URL` — unchanged from above, and notes 1-5 above all still apply. The
Toolkit repository contains a ready-to-run two-instance example,
`docker-compose.multi-instance-net.yml`.

**Links inside emails.** `${serverAddress}` in mail templates and custom code — the base of
every ticket link the app sends out — comes from `NEXTAUTH_URL` as before (its `/api/auth`
suffix is dropped), or from `App__BaseUrl` if you prefer the new name. Keep it per instance, the
same way the old stack file spelled it: `https://toolkitBaseUrl.com/instance1`. If it is left at
the bare domain the instance path is added automatically, so an emailed link still opens the
right instance.

**Compression note.** The older setup had to disable gzip compression to rewrite responses.
The .NET setup does not, so compression is enabled again and the UI loads noticeably less data
over the network.

#### Migration checklist

To move an existing multi-instance deployment onto the .NET image, this is the whole list:

| | Change | How many |
|---|---|---|
| 1 | The image tag on the Toolkit services **and** on the Nginx service (`nginx-<same version>`). Using one stack variable for both — `…:${TOOLKIT_IMAGE_TAG}` and `…:nginx-${TOOLKIT_IMAGE_TAG}` — makes this a single edit. | 1 |
| 2 | `DATABASE_URL` → `ConnectionStrings__DefaultConnection` (note 5 above). | one per instance |
| 3 | **Check** that `STORAGE_PATH` is an absolute path matching the volume mount, e.g. `/app/storage`. A relative value still works — the container resolves it the same way the old image did and logs what it resolved to — but spell it out. | check only |
| 4 | **Check** the Keycloak client's "Valid redirect URIs". The web UI now returns to `/instanceN/auth/callback` instead of `/instanceN/api/auth/...`, so a wildcard such as `https://your.domain.com/*` already covers it and needs no change; a narrower entry does. | usually none |
| 5 | **Recommended:** `DATABASE_BACKUP_PATH` — a directory on the SQL Server host. The first start of the new image applies database migrations; with this set it backs up the database first (see "Database" above). | optional, one per instance |

Everything else in an existing stack file keeps working as it is, including `BASE_PATH`,
all `KEYCLOAK_*`, `SERVICE_CLIENT_*`, `WORKFLOW_*`, `SUPPORT_*`, `ENABLE_CRON`,
`NEXT_PUBLIC_TICKET_PREFIX` and `NEXTAUTH_URL`. Variables that no longer do anything
(`NEXTAUTH_SECRET`, `BODY_SIZE_LIMIT`, `DEVICE_ID`, `INSTANCE_NAME`, `NODE_ENV`,
`STORAGE_TYPE`, `SERVICE_ACCOUNT_SECRET`, …) can be left in place; the container lists them on
startup so you can clean them up later.

To see exactly what a stack file resolves to **before** deploying it, without starting anything:

```bash
docker run --rm --env-file instance1.env quay.io/caxperts/toolkit.net:<tag> --translate-only
```

It prints every setting the container resolved, with secrets masked, and warns about anything it
could not translate — including a redirect URL or email link base that is missing its instance
subpath.

**Licensing.** Each instance still consumes one seat per signed-in user, so a user working in two
instances holds two seats, exactly as before. If used from within UDiTH no additional license is consumed. 


### Configuring Nginx Proxy Manager

If you use Nginx Proxy Manager (recommended) instead of directly exposing the nginx service e.g. via setting the PORT section to Port: -80:80 then make sure the following settings are set and match the values specified in the docker-compose file:

*Note:* This section assumes you are already using Nginx Proxy Manager to host a single-instance deployment of the toolkit and therefore only covers the necessary changes to migrate to a multi-tenant deployment.

The following needs to changed in the Proxy Host:

**Forward Hostname / IP**: toolkit_entrypoint <- must be the same value as the service name of the nginx service in the docker-compose file

**Forward Port**: 80 <- this is the port the nginx service listens on

The rest is the same as for the single instance deployment e.g. the 
**Custom Nginx Configuration** is also set to:
```
proxy_buffers 16 16k;
proxy_buffer_size 32k;
proxy_busy_buffers_size 64k;
```

### Custom Nginx landing page

Per default a simple landing page containing links to all available instances is created that looks like this:

<img src="media/deployment_guide_nginxLandingPage_default.png" width="500">

In order to set your own index.html file that for example contains your corporate identity or an automatic redirect to a specific instance do the following.

Put your custom index.html file in the same folder as the docker-compose.yml file. Than uncomment the volume mapping in the docker-compose.yml file: 

before -> leads to generation of default landing page
```
# volumes:
    #   - ./index.html:/usr/share/nginx/html/index.html:ro
```
after -> your specified index.html file is used instead of the auto-generated one:

```
volumes:
      - ./index.html:/usr/share/nginx/html/index.html:ro
```

Here is an example how this can look like:

<img src="media/deployment_guide_nginxLandingPage_custom.png" width="500">

---

# Toolkit App for UDiTH — Custom Code and Macro Reference

# Custom Code and Macro Reference

This document provides a complete reference for the ExecutorCustomCode macro system, including workflow step macros, event listeners, and field validators. It covers all available capabilities, how to use them, and their current implementation status.

> **Related Administration Pages:**
>
> - [Workflows](#1-workflow-step-macros) &mdash; Settings > Workflows
> - [Event Listeners](#2-workflow-level-event-macros) &mdash; Settings > Listeners
> - [Field Validators](#field-validators-valuechangevalidating) &mdash; Settings > Workflows > Validators
> - [Custom Code Editor](#custom-code-editor) &mdash; Embedded in Workflows, Listeners, and Validators pages

---

# 1. Macro Requirements

## A. One Macro per Workflow Step

Each workflow step can have a **Custom Code** block that executes when the step is accepted or denied.

**How to configure:**

1. Navigate to **Settings > Workflows**
2. Select a workflow from the list
3. Click the **Code** cell for the desired workflow step
4. Write JavaScript code in the integrated code editor
5. Click **Compile** to verify syntax, then **Save**

**How it works:**

- The macro runs when the workflow step is triggered (accepted or denied)
- The `userAction.Result` object tells you whether the step was accepted (`ActionResult.Yes`) or denied (`ActionResult.No`)
- Each step can have exactly one code block

**Example:**

```javascript
if (userAction.Result === ActionResult.Yes) {
  await setStatus("Approved");
  await sendMailTo(
    ["manager@example.com"],
    "Step approved for Ticket #" + UniqueID,
    "Workflow Progress"
  );
} else {
  await stepBackwardJumpTo(1, "Returned to initial review");
}
```

> **Note:** In addition to workflow step macros, you can use [Event Listeners](#2-workflow-level-event-macros) for event-driven automation that is not tied to a specific step action.

---

## B. Workflow-Level Event Macros

Event listeners enable macros that trigger on specific events across the application. Unlike step macros, event listeners react to conditions such as field changes, email receipts, and scheduled intervals.

**How to configure:**

1. Navigate to **Settings > Listeners**
2. Click the **Add (+)** button
3. Select a **Type** from the dropdown
4. Configure the required fields for that type
5. Write Custom Code and save

### Available Event Types

| Event Type | Trigger | Required Fields |
|---|---|---|
| `onWorkflowStepAccepted` | A workflow step is accepted | Checked Field (step number), Checked Value, Workflow ID |
| `onValueChanged` | A ticket field value matches a condition | Checked Field (field name), Checked Value |
| `onTicketCreated` | A new ticket is created | Default (typically `true`) |
| `onInteractionEmailSent` | An interaction email is sent | Subject Regex, Content Regex |
| `onTicketOverdue` | Scheduled check for overdue tickets | Checked Field (date field), Overdue (ms) |
| `onEmailReceived` | An inbound email is received | Subject Regex, Content Regex |

> **Note:** The **Interval** field is stored but not used. Scheduled checks run at fixed server
> intervals: overdue tickets every 5 minutes (`Worker:ListenerIntervalMinutes`), inbound email
> every minute (`Worker:EmailIngestionIntervalMinutes`).

> **Important — a listener runs only once per ticket.** After a listener has run for a ticket,
> it does not run for that ticket again, even if its condition becomes true again later (for
> example, the same step is accepted a second time). The run is recorded even if the custom code
> fails. `onEmailReceived` and `onInteractionEmailSent` listeners are the exception: they run for
> every matching email.

### ValueChangeValidating

Field validators provide a pre-save validation event. When a ticket field value changes, all active validators for that field run **before** the value is saved.

**How to configure:**

1. Navigate to **Settings > Workflows > Validators**
2. Select a workflow from the dropdown
3. Click **Add** to create a new validator
4. Select one or more fields from the **Fields** TagBox
5. Enter an **Error Message** (shown to the user on failure)
6. Write validation code that returns `true` (allow) or `false` (block)

See [Field Validators](#field-validators-valuechangevalidating) for full details.

### ValueChanged (onValueChanged)

Triggers **after** a ticket field value changes to a specified value. Unlike validators, listeners cannot block the change.

**Configuration example:**

```
Type: onValueChanged
Checked Field: Severity
Checked Value: Critical
Custom Code:
  await stepForwardJumpTo(5, "Critical severity detected - escalating");
```

### EmailReceived

Triggers when an inbound email matches regex patterns on subject and/or body content.

**Configuration example:**

```
Type: onEmailReceived
Subject Regex: (Out of Office|Automatic reply)
Content Regex: .*
Custom Code:
  console.log("Auto-reply detected");
```

---

# 2. Required Capabilities

## A. Detecting Received Emails

The system can detect inbound emails with specific content using regex pattern matching.

**How to configure:**

1. Navigate to **Settings > Listeners**
2. Add a listener with **Type** = `onEmailReceived`
3. Set **Subject Regex** (regex pattern to match the email subject)
4. Set **Content Regex** (regex pattern to match the email body)
5. Write Custom Code to execute when a matching email is found

**Use cases:**

- Detect autoresponder / out-of-office replies: `Subject Regex: (?i)(Out of Office|Automatic reply|Abwesenheit)`
- Detect approval keywords in email body: `Content Regex: (?i)(approved|confirmed)`
- Set ticket fields from values in the email (via `getRegexMatches()`)

**How it works internally:**

- A background job reads new emails from the support mailbox every minute (server setting
  `Worker:EmailIngestionIntervalMinutes`). The listener's **Interval** field is not used.
- Each email is linked to a ticket: the ticket named by `[#123]` at the end of the subject, else the
  ticket of the same email conversation, else a new ticket. The listener code runs for that ticket.
- An email that was already imported (same mail ID) is skipped, so it is never processed twice.
- Every enabled `onEmailReceived` listener is checked, independent of the ticket's workflow.
- An empty Subject Regex or Content Regex matches everything. An invalid pattern is compared as
  plain text.
- When matched, the listener's Custom Code executes with full access to the macro API. It runs
  without a user, so `createComment` does nothing (a server warning is logged).

**Email-specific helpers** (only in `onEmailReceived` and `onInteractionEmailSent` listeners):

| Function | Returns |
|---|---|
| `getEmailData()` | `{ from, subject, body }` of the received or sent email (`body` is the HTML body; for a sent email, `from` is the support mailbox). `null` in all other listener types |
| `getRegexMatches()` | Array of the capture groups from the Subject Regex match, then from the Content Regex match. `[]` in all other listener types |

```javascript
// Subject Regex: Order (\d+)
const e = getEmailData();
const m = getRegexMatches();
await setTicketValue("Order No", m[0]);
await sendMailTo(["orders@example.com"], "Mail from " + e.from, "Order " + m[0] + " received");
```

---

## B. Detecting Ticket Value Changes

Two systems detect ticket value changes:

### System 1: Event Listeners (post-change)

Listeners of type `onValueChanged` check if a field has a specific value after a change.

**Configuration:**

| Field | Value |
|---|---|
| Type | `onValueChanged` |
| Checked Field | Field name (e.g., `Severity`) |
| Checked Value | Target value (e.g., `Critical`) |
| Workflow ID | Target workflow (or set Default = true for all) |

**Example: Route to different workflow based on severity**

```javascript
// Listener: onValueChanged, CheckedField: "Severity", CheckedValue: "Critical"
await stepForwardJumpTo(5, "Escalated due to critical severity");
await sendMailTo(
  ["escalation-team@example.com"],
  "Ticket has been escalated to critical severity.",
  "Critical Escalation"
);
```

### System 2: Field Validators (pre-change)

Validators run **before** the value is saved and can **block** the change.

**Example: Different validation based on severity**

```javascript
// Validator on "Severity" field
// ErrorMessage: "Only managers can set severity to Critical"
if (getCurrentFieldValue() === "Critical") {
  return hasRole("Manager");
}
return true;
```

---

## C. User Rights

Custom code can access the current user's roles to enforce permission-based logic.

### In Field Validators

The `hasRole(roleName)` function is available and checks whether the current user has a specific role.

**Example:**

```javascript
// Only users with "Editor" role can modify this field
if (!hasRole("Editor")) {
  return false; // ErrorMessage: "You don't have the right to modify this value."
}
return true;
```

**Available helper:**

| Function | Description |
|---|---|
| `hasRole(roleName)` | Returns `true` if the current user has the named role, `false` otherwise |

**Available variable:**

| Variable | Description |
|---|---|
| `userRoles` | Array of the current user's role names (e.g., `["Admin", "Editor"]`) |

### In Workflow Step Macros and Event Listeners

User roles are **not** directly available in workflow macros. The executor receives `userID` but roles are not pre-loaded into the execution context.

---

# 3. Actions Within a Macro

## Trigger a Jump to a Certain Workflow Step

Custom code can override the default forward/backward step configured in the workflow table.

### stepForwardJumpTo(stepNumber, message?)

Jumps to the specified step number as a **success/approval** action. Overrides the Forward Step ID column.

```javascript
// Skip to step 5 with an activity log message
await stepForwardJumpTo(5, "Auto-approved based on priority");
```

### stepBackwardJumpTo(stepNumber, message?)

Jumps to the specified step number as a **failure/denial** action. Overrides the Backward Step ID column.

```javascript
// Send back to step 1 for rework
await stepBackwardJumpTo(1, "Additional information required");
```

> **Note:** When these functions are used, they take **priority over the Forward Step ID and Backward Step ID values** defined in the workflow table.

> **Note:** The message is posted to the activity chat under the acting user once the jump has
> advanced. A script running without a user (an auto-executed `None` step triggered by attaching
> the workflow, or by the Worker) has no author for the comment — the message is written to the
> server log instead.

> **Note — jumps in event listeners:** A listener has no step of its own, so the jump moves the
> ticket's **pending** step. A listener with a Workflow ID moves the pending step of that workflow.
> A Default listener moves the pending step only when the ticket has exactly one pending step;
> otherwise nothing moves. A failed script does not jump. `onTicketOverdue` and `onEmailReceived`
> listeners cannot jump — the request is only written to the server log.

### Staying on the Current Step

A jump to the step's **own** step number means "stay here". The step is not accepted or denied,
no new step is created, and the step stays pending. Use this when a precondition is not met yet.

```javascript
// Step 1: do not move on until the CTR discipline is filled in
const ctr = await getTicketValue("CTR Discipline First Action Owner");

if (!ctr) {
  await showPopup("Please fill in 'CTR Discipline First Action Owner' before approving.");
  await stepBackwardJumpTo(1); // this code runs on step 1 -> stay on step 1
  return false;
}

await stepForwardJumpTo(2);
return true;
```

> **Note:** The user sees no "Step approved" message when the step stays. Tell them what to do
> with [`showPopup`](#showpopupmessage) — otherwise the click appears to do nothing. On a `None`
> step a self-jump simply stops the automatic execution; the step waits.

---

## Refuse to Modify a Value

Field validators can prevent a value from being saved by returning `false`.

**How it works:**

1. User changes a field value
2. All active validators for that field execute in order
3. If any validator returns `false`, the change is **blocked**
4. The configured **Error Message** is shown as a toast notification
5. The field retains its previous value

**Example: Prevent unauthorized changes**

```javascript
// Validator on "Budget" field
// ErrorMessage: "You don't have the right to modify this value."
if (!hasRole("Finance")) {
  return false;
}
return true;
```

**Example: Conditional refusal**

```javascript
// Validator on "Status" field
// ErrorMessage: "Cannot close ticket without a resolution."
if (getCurrentFieldValue() === "Closed") {
  const resolution = getFieldValue("Resolution");
  return resolution && resolution.trim() !== "";
}
return true;
```

---

# 4. Misc Functions

## A. Show Message

### In Field Validators

When a validator returns `false`, the **Error Message** configured on the validator is displayed to the user as a toast notification. This is the primary mechanism for showing messages during field validation.

**How to configure:**

1. Navigate to **Settings > Workflows > Validators**
2. Create or edit a validator
3. Set the **Error Message** field (max 500 characters)
4. Write validation code that returns `false` when the message should be shown

**Example:**

```
Error Message: "You don't have the right to modify this value."
Custom Code: return hasRole("Editor");
```

### In Workflow Step Macros

#### showPopup(message)

Shows a popup with `message` to the user whose action ran the code. The popup opens after the
step has been processed — on the Approvals tab and on the Kanban board. For an automatic `None`
step the user is the one who attached the workflow, or who accepted/denied the step before it.

```javascript
// Tell the approver what happened next
await showPopup("The ticket was forwarded to the modelling discipline.");
return true;
```

**Example: Explain why the step did not move on**

```javascript
const description = await getTicketValue("Tag Description");

if (!description) {
  await showPopup("Add a Tag Description first. The step stays open.");
  await stepBackwardJumpTo(3); // this code runs on step 3 -> stay on step 3
  return false;
}
return true;
```

**Example: Several lines**

```javascript
// Each call adds a line to the same popup
await showPopup("Checks passed:");
await showPopup("- Tag Description present");
await showPopup("- CTR discipline: " + (await getTicketValue("CTR Discipline First Action Owner")));
return true;
```

**Behaviour:**

- Several calls are shown in **one** popup, one line each, in call order.
- The text is shown as plain text. HTML tags in the message are not rendered.
- Messages from a chain of automatic `None` steps are shown together with the message of the
  step the user clicked, in execution order.
- The popup needs a user in a browser. An event listener, or a `None` step run by the server
  without a user, has none: the message is written to the server log and not shown.
- **Compile** prints `showPopup: <message>` in the Compile Result instead of opening a popup.
- If the script fails, the error is shown instead and the popup is not.

Console output via `console.log()` is captured during compilation/testing but is not displayed to
the user in production mode.

---

## B. Sending Emails

The `sendMailTo()` function sends emails via the Microsoft Graph API.

### Function Signature

```javascript
await sendMailTo(recipients, message, subject, attachments?)
```

| Parameter | Type | Description |
|---|---|---|
| `recipients` | `string[]` | Array of email addresses |
| `message` | `string` | HTML email body (supports template variables) |
| `subject` | `string` | Email subject line (supports template variables) |
| `attachments` | `any[]` (optional) | Array of attachments (PDF reports, UPVF files) |

### Basic Usage

```javascript
await sendMailTo(
  ["d.dolinsky@caxperts.com", "daniel.dolinsky@dolinsky.de"],
  "Ticket has been approved.",
  "Ticket Approval Notification"
);
```

### HTML Email Body

The message parameter accepts full HTML:

```javascript
await sendMailTo(
  ["team@example.com"],
  `<h2>Ticket Update</h2>
   <p>Ticket <strong>#${UniqueID}</strong> has been approved.</p>
   <ul>
     <li><strong>Status:</strong> Approved</li>
     <li><strong>Date:</strong> ${Date("MMMM dd, yyyy")}</li>
   </ul>
   <p><a href="${serverAddress}/tickets/${UniqueID}">View Ticket</a></p>`,
  "Ticket Approval Notification"
);
```

### With PDF Attachment

```javascript
const report = await createPdfReport("ReportTemplate");
await sendMailTo(
  ["user@example.com"],
  "Please see the attached report.",
  "Monthly Report",
  report
);
```

### With UPVF File Attachment

```javascript
const upvfFile = await extractUpvf("drawing.upvf");
attachments.push(upvfFile);
await sendMailTo(
  ["user@example.com"],
  "UPVF file attached.",
  "Document Delivery",
  attachments
);
```

### With Multiple Attachments (PDF + UPVF)

```javascript
attachments.push(await createPdfReport("Template"));
attachments.push(await extractUpvf());
await sendMailTo(
  ["user@example.com"],
  "Report and UPVF file attached.",
  "Combined Delivery",
  attachments
);
```

> **Note:** The `attachments` array is pre-defined in the execution context. You can push items to it and pass it to `sendMailTo`.

### Template Variables in Emails

Email message and subject support `${expression}` template variable syntax. See [String Replacements](#c-string-replacements) for the full list of supported variables.

> **Environment behavior:** In production runs the mail is sent through the configured mail
> service. The Compile/Test button never sends — it prints the recipients and the fully expanded
> subject and body into the Compile Result output instead, which is where an unresolved `${…}`
> shows up.

---

## C. String Replacements

The system uses ES6 template literal syntax `${expression}` for dynamic variable substitution in email templates and workflow email messages.

### Supported Variables

| Pattern | Example Output | Description |
|---|---|---|
| `${UniqueID}` | `123` | Ticket unique ID |
| `${PunchItem}` | `123` | Ticket punch item ID |
| `${Status}` | `Open` | Ticket status (built-in property, not a ticket field) |
| `${Details["Field Name"]}` | `Broken pipe` | Any ticket field value (configured ticket fields only) |
| `${Details.Subject}` | `Leak in area B` | Shorthand for field access (no spaces in name) |
| `${serverAddress}` | `https://toolkit.example.com` | Application server URL (from `NEXTAUTH_URL`; `App__BaseUrl`/`KEYCLOAK_REDIRECT_URL` on the .NET stack) |
| `${Date("format")}` | `2025-12-01` | Current date with format string |
| `${FormatDate(value, "format")}` | `01/12/2025` | Format any date value |
| `${ParseFormatDate(str, inFmt, outFmt)}` | `2025-12-01` | Parse a date string and reformat it |
| `${Workflow[1].Note}` | `Manager approved` | Workflow step note (by step number) |
| `${Workflows[0].Status}` | `Accepted` | Workflow step status (by array index, legacy) |

### Date Format Patterns

| Format | Example |
|---|---|
| `yyyy-MM-dd` | 2025-12-01 |
| `MM/dd/yyyy` | 12/01/2025 |
| `dd-MM-yyyy` | 01-12-2025 |
| `yyyy-MM-dd HH:mm:ss` | 2025-12-01 14:30:00 |
| `MMMM dd, yyyy` | December 01, 2025 |
| `dd.MM.yyyy` | 01.12.2025 |

### Usage in Custom Code

The `Date` function is directly available in custom code. It supports two syntaxes:

```javascript
// As a template variable (calls toString, returns yyyy-MM-dd)
const msg = `Today is ${Date}`;

// As a function call (with optional format string)
await setTicketValue("ApprovalDate", Date("yyyy-MM-dd"));
await setTicketValue("Timestamp", Date("yyyy-MM-dd HH:mm:ss"));
```

### FormatDate and ParseFormatDate

These helper functions are available in custom code for formatting arbitrary dates:

```javascript
// FormatDate(dateInput, formatString?) - format any date value
const formatted = FormatDate("2025-12-01", "dd/MM/yyyy"); // "01/12/2025"
const fromDate = FormatDate(new Date(), "MMMM dd, yyyy"); // "February 13, 2026"

// ParseFormatDate(dateString, inputFormat, outputFormat?) - parse and reformat
const result = ParseFormatDate("01-12-2025", "dd-MM-yyyy", "yyyy/MM/dd"); // "2025/12/01"
```

### serverAddress in Custom Code

The `serverAddress` and `UniqueID` variables are directly available in custom code for building full ticket URLs.

**Note:** `serverAddress` **already includes the instance path** (e.g., `/instance2`), so you don't need to append it separately. It is derived from `NEXTAUTH_URL`, and on the .NET stack from `App__BaseUrl` (falling back to `KEYCLOAK_REDIRECT_URL`) plus `BASE_PATH` — so a script written for either stack builds the same URL.

```javascript
// Build full ticket URL (serverAddress already includes instance path)
const ticketUrl = `${serverAddress}/tickets/${UniqueID}`;
// Example: https://toolkit-dev.udith.io/instance2/tickets/102

await sendMailTo(
  ["user@example.com"],
  `<p><a href="${ticketUrl}">View Ticket #${UniqueID}</a></p>`,
  "Ticket Link"
);

await sendMailTo(
  ["team@example.com"],
  `<h2>Ticket Update</h2>
   <p>Ticket <strong>#${UniqueID}</strong> has been approved.</p>
   <p><a href="${serverAddress}/tickets/${UniqueID}">View Ticket</a></p>`,
  "Ticket Approval Notification"
);
```

| Function | Parameters | Description |
|---|---|---|
| `Date(format?)` | `format`: date-fns format string (default: `yyyy-MM-dd`) | Current date. Works as `${Date}` or `Date("format")` |
| `FormatDate(dateInput, format?)` | `dateInput`: Date, string, or timestamp; `format`: output format (default: `yyyy-MM-dd`) | Format any date value. Returns `""` on invalid input |
| `ParseFormatDate(str, inFmt, outFmt?)` | `str`: date string; `inFmt`: input format; `outFmt`: output format (default: `yyyy-MM-dd`) | Parse a date string with known format and reformat it |

### Usage in Email Templates

Template variables are used in Workflow Email Messages (configured in the workflow table) and in `sendMailTo()` message/subject parameters:

```html
<h2>Ticket Update Notification</h2>
<p>Ticket <strong>#${PunchItem}</strong> has been updated.</p>
<ul>
  <li><strong>Area System:</strong> ${Details["Area System"]}</li>
  <li><strong>Date:</strong> ${Date("MMMM dd, yyyy")}</li>
</ul>
<p><a href="${serverAddress}/tickets/${PunchItem}">View Ticket</a></p>
```

### Safe Proxies

Missing or undefined variables return an empty string instead of throwing an error. For example, `${NonExistentField}` renders as `""`.

---

## D. Ticket Read/Write (including .upvf-Blobs)

Custom code has full read and write access to ticket data and file attachments.

### Reading Ticket Data

| Function | Description |
|---|---|
| `await getTicketValue(fieldName)` | Returns the value of a single ticket field |
| `await getPunchlistDetails()` | Returns an **array** of value rows: `{ PunchlistDetails: { Detail }, Value, PunchDetailID }` |
| `await getTicketComponents()` | Returns an **array** of the model components attached to the ticket, ordered by `ID`. Each entry is the full `Components` row — currently `{ ID, UniqueID, UID, CompName, BoundingBox, DrawingName, ComponentView, AssignedDate }`; new columns appear automatically. `ComponentView` is `"2D"` for P&ID components; anything else (including `null`) is 3D. |
| `await getTicketComponent(columnName)` | Returns an **array** with one value per component — the named column of each row from `getTicketComponents()`, in the same order. An unknown column name yields `null` for every component. |

**Examples:**

```javascript
const tagNo = await getTicketValue("Tag No");
console.log("Tag Number:", tagNo);

const rows = await getPunchlistDetails();
const stage = rows.find(r => r.PunchlistDetails.Detail === "Stage")?.Value;
console.log("Stage:", stage);

const components = await getTicketComponents();
const names3D = components.filter(c => c.ComponentView !== "2D").map(c => c.CompName);
console.log("3D components:", names3D.join(", "));

const drawings = await getTicketComponent("DrawingName");
console.log("Drawings:", drawings.filter(Boolean).join(", "));
```

> **Note:** `getTicketValue()` and `getPunchlistDetails()` read only the configured ticket
> fields. Built-in ticket properties such as `PunchItem` and `Status` are not ticket fields:
> `getTicketValue("PunchItem")` and `getTicketValue("Status")` return `null`. Use the `UniqueID`
> variable for the ticket number. (The server-side placeholders `${PunchItem}` and `${Status}`
> do work in `sendMailTo` message and subject text written in normal quotes, e.g.
> `'Ticket ${PunchItem} is ${Status}'` — see [String Replacements](#c-string-replacements). In a
> backtick string JavaScript evaluates `${…}` itself, where these names are not defined.)

> **Note:** `getPunchlistDetails()` returns an array of rows, not a name→value object — for a
> single field, `getTicketValue()` is the simpler call. (Only field *validators* get a name→value
> object, via `getAllFields()`.)

### Writing Ticket Data

| Function | Description |
|---|---|
| `await setTicketValue(fieldName, value)` | Writes a field's value (creates the value if unset; **throws** when the field itself does not exist) |
| `await updatePunchlistDetails(data)` | Batch-updates multiple fields (an unknown field name is skipped with a server-side warning) |
| `await createHiddenField(data)` | Writes a field's value and, when the field does not exist yet, **creates it as a hidden field** first |

**Examples:**

```javascript
await setTicketValue("Status Comment", "Auto-approved by macro");
await setTicketValue("ApprovalDate", Date("yyyy-MM-dd"));

await updatePunchlistDetails([
  { "Tag No": "P-12345" },
  { "Unit No": "U-001" }
]);

await createHiddenField({ "InternalFlag": "processed" });
```

### UPVF File Operations

| Function | Description |
|---|---|
| `await extractUpvf(fileName?)` | Extracts a UPVF file as a Buffer object |
| `await deleteUpvf(fileID)` | Soft-deletes a UPVF file (sets Hidden = true) |

**ExtractUpvf** returns an object with:

```javascript
{
  filename: "drawing.upvf",        // File name
  content: Uint8Array,             // File bytes (decoded lazily on first access)
  contentType: "application/json", // MIME type
  fileInfo: {
    ID: 42,                        // File ID (use with DeleteUpvf)
    FileName: "drawing.upvf",
    DateTimeCreated: Date
  }
}
```

> **Note:** `content` is a `Uint8Array` (the old Node executor returned a `Buffer`);
> `content.length` and indexed access work the same. When the object is only pushed into
> `attachments` for `sendMailTo`, the bytes are attached server-side and `content` is never
> decoded.

**Examples:**

```javascript
// Extract and inspect a UPVF file
const upvf = await extractUpvf("drawing.upvf");
console.log(upvf.filename, upvf.content.length, "bytes");

// Extract and attach to email
attachments.push(await extractUpvf());
await sendMailTo(["user@example.com"], "UPVF attached", "File Delivery", attachments);

// Extract, process, then delete
const upvf = await extractUpvf();
if (upvf) {
  // ... process file ...
  await deleteUpvf(upvf.fileInfo.ID);
}
```

### PDF Report Generation

| Function | Description |
|---|---|
| `await createPdfReport(templateName?)` | Renders the stored ticket report template for the current ticket and returns an attachment handle |

How it works on the .NET stack:

- The **stored pdfme report template** (Administration → Tickets → Ticket Report) is rendered
  server-side against the same `reportData.*` values the Report button uses — one page, with the
  ticket's `Initial`/`Final` snapshots and workflow signatures. The `templateName` argument is
  ignored: there is only one stored template (the old executor ignored it too).
- The renderer draws the same field subset as the Report button (text, multiVariableText, image,
  line, rectangle — anything else renders blank) and supports the `#` alias suffix and an embedded
  base PDF.
- The return value is an attachment handle `{ filename, contentType, __attachmentRef }` — pass it
  to `sendMailTo` (directly or via `attachments`). The PDF bytes stay server-side.
- **No template configured** renders the same placeholder page the Report button shows ("Ask your
  admin for a valid template configuration.").
- **On a rendering error** the function returns `null` (logged server-side);
  `sendMailTo(..., report)` then sends the mail without the attachment instead of failing the
  script.

**Example:**

```javascript
const report = await createPdfReport();
await sendMailTo(
  ["user@example.com"],
  "Report attached",
  "Ticket Report",
  report
);
```

---

## E. Reading Current Language

The current user's language is available via the `language` variable. The language is sourced from the application's language setting (configured per user in the application UI). If no language is set, it defaults to `"en-US"`.

### In All Contexts (Macros, Listeners, Validators)

```javascript
console.log(language); // e.g., "en-US", "ko", "de"

const message = language === "ko" ? "승인됨" : "Approved";
await setTicketValue("StatusLabel", message);
```

> **Note:** The `language` variable is always defined and never `undefined`. It defaults to `"en-US"` when no language is configured.

---

## F. HTTP(S) POSTs and GETs

Custom code can make HTTP requests using a `fetch` helper shaped like the standard API.

> **Allowlisted.** Custom code runs with the server's network position, so `fetch` is **disabled
> until an administrator lists allowed hosts** in the `CustomCode:Fetch` configuration
> (`CustomCode__Fetch__AllowedHosts__0=api.example.com`, or `.example.com` to include subdomains).
> Private/loopback targets additionally require `AllowPrivateNetworks`; every redirect hop is
> re-validated. A rejected request throws a catchable JS error. The old Node executor exposed the
> global `fetch` with no restrictions — see `REQUIRED_DOCS/EVENT_LISTENERS.md`.

### GET Request

```javascript
const response = await fetch("https://api.example.com/data");
const data = await response.json();
console.log(data);
```

### POST Request

```javascript
const response = await fetch("https://api.example.com/webhook", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    ticketId: UniqueID,
    stage: await getTicketValue("Stage"),
  }),
});
const result = await response.json();
console.log("Webhook response:", result);
```

### POST with Custom Headers

```javascript
const response = await fetch("https://internal-api.example.com/notify", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-API-Key": "your-api-key",
  },
  body: JSON.stringify({
    message: "Ticket updated",
  }),
});

if (response.ok) {
  console.log("Notification sent successfully");
} else {
  console.log("Notification failed:", response.status);
}
```

### Practical Example: Sync with External System

```javascript
// When a ticket is approved, notify an external ERP system
if (userAction.Result === ActionResult.Yes) {
  const ticketData = {
    id: UniqueID,
    tagNo: await getTicketValue("Tag No"),
    status: "Approved",
    approvedDate: Date("yyyy-MM-dd"),
  };

  try {
    const response = await fetch("https://erp.example.com/api/tickets", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(ticketData),
    });

    if (response.ok) {
      await setTicketValue("ERP Sync Status", "Synced");
    } else {
      await setTicketValue("ERP Sync Status", "Failed: " + response.status);
    }
  } catch (error) {
    await setTicketValue("ERP Sync Status", "Error: " + error.message);
  }
}
```

> **Note:** HTTP requests execute server-side. Ensure target URLs are reachable from the server
> environment **and covered by the `CustomCode:Fetch` allowlist** (private/internal addresses
> additionally need `AllowPrivateNetworks`).

---

# Field Validators (ValueChangeValidating)

## Overview

Field validators enforce custom business rules **before** field values are saved. When a user changes a ticket field, all active validators for that field execute. If any validator returns `false`, the change is blocked and the configured error message is displayed.

**Navigate to:** Settings > Workflows > Validators

## Validation-Specific Functions

These functions are available **only** in field validator custom code:

| Function | Description |
|---|---|
| `getCurrentFieldValue()` | Returns the new value the user is trying to save |
| `getFieldValue(fieldName)` | Returns the current value of any ticket field |
| `getAllFields()` | Returns an object with all field names and values |
| `hasRole(roleName)` | Returns `true` if the current user has the named role |

## Available Variables

| Variable | Description |
|---|---|
| `currentValue` | The value being validated (same as `getCurrentFieldValue()`) |
| `fieldName` | Name of the field being validated |
| `punchDetails` | Object with all field values (same as `getAllFields()`) |
| `userRoles` | Array of the current user's role names |
| `language` | Current user's language code (defaults to `"en-US"`) |

## Quick Examples

**Format validation:**

```javascript
// Ensure uppercase only
return getCurrentFieldValue() === getCurrentFieldValue().toUpperCase();
```

**Role-based restriction:**

```javascript
// Only admins can set to "Critical"
if (getCurrentFieldValue() === "Critical") {
  return hasRole("Admin");
}
return true;
```

**Cross-field validation (select both fields in the Fields TagBox):**

```javascript
// End Date must be after Start Date
if (fieldName === "End Date") {
  const startDate = new Date(getFieldValue("Start Date"));
  const endDate = new Date(getCurrentFieldValue());
  return endDate >= startDate;
}
return true;
```

---

# Custom Code Editor

The integrated code editor is available in Workflows, Listeners, and Validators pages. It provides:

- **Syntax highlighting** for JavaScript
- **Compile** button to check for syntax errors
- **Test** button to execute code with mock data (no side effects)
- **Function descriptions** panel showing all available APIs and their signatures
- **Console output** panel showing `console.log()` output during testing
- **PunchlistID for compilation** field to test with a specific ticket's data

## Available Functions Summary

### Ticket Management

| Function | Context | Description |
|---|---|---|
| `await getTicketValue(fieldName)` | All | Read a ticket field value |
| `await setTicketValue(fieldName, value)` | Macros, Listeners | Write a ticket field value |
| `await getPunchlistDetails()` | All | Get all ticket field values |
| `await updatePunchlistDetails(data)` | Macros, Listeners | Batch update ticket fields |
| `await createHiddenField(data)` | Macros, Listeners | Create a hidden ticket-specific field |

### Workflow Control

| Function | Context | Description |
|---|---|---|
| `await setStatus(status)` | Macros, Listeners | Set the ticket status |
| `await stepForwardJumpTo(step, message?)` | Macros, Listeners | Jump forward (approval). The step's own number = stay on the step |
| `await stepBackwardJumpTo(step, message?)` | Macros, Listeners | Jump backward (denial). The step's own number = stay on the step |
| `await closeWorkflow()` | Macros, Listeners | Close the workflow and ticket |
| `await finishWorkflow()` | Macros, Listeners | Mark the workflow as finished |

### Communication

| Function | Context | Description |
|---|---|---|
| `await sendMailTo(to, message, subject, attachments?)` | Macros, Listeners | Send an email |
| `await createComment(message)` | Macros, Listeners | Create a ticket comment |
| `await showPopup(message)` | Macros | Show a popup to the user who accepted/denied the step — see [Show Message](#a-show-message). Ignored in listeners and auto-executed `None` steps |

### File Operations

| Function | Context | Description |
|---|---|---|
| `await createPdfReport(templateName?)` | Macros, Listeners | Render the stored report template; returns an attachment handle (`templateName` is ignored — the template is a singleton) |
| `await extractUpvf(fileName?)` | Macros, Listeners | Extract a UPVF file |
| `await deleteUpvf(fileID)` | Macros, Listeners | Soft-delete a UPVF file |

### Validation (Field Validators only)

| Function | Context | Description |
|---|---|---|
| `getCurrentFieldValue()` | Validators | Get the value being validated |
| `getFieldValue(fieldName)` | Validators | Get any field's current value |
| `getAllFields()` | Validators | Get all field values as an object |
| `hasRole(roleName)` | Validators | Check if user has a role |

### Utilities

| Function | Context | Description |
|---|---|---|
| `Date` / `Date(format?)` | All | Current date. Use as `${Date}` or `Date("format")` (default: `yyyy-MM-dd`) |
| `FormatDate(dateInput, format?)` | All | Format any date value (default: `yyyy-MM-dd`) |
| `ParseFormatDate(str, inFmt, outFmt?)` | All | Parse a date string and reformat it |
| `fetch(url, options?)` | All | Make HTTP requests (GET, POST, etc.) — allowlisted, see [HTTP(S) POSTs and GETs](#f-https-posts-and-gets) |

### Available Objects

| Object | Context | Description |
|---|---|---|
| `userAction.Result` | Macros | The step action result: `ActionResult.Yes` (accepted), `ActionResult.No` (denied), `ActionResult.Undefined` (auto-executed `None` step) |
| `ActionResult` | Macros | Enum: `Yes`, `No`, `Undefined` |
| `language` | All | Current user's language code (defaults to `"en-US"`) |
| `serverAddress` | All | Application server URL from `NEXTAUTH_URL` (.NET stack: `App__BaseUrl`/`KEYCLOAK_REDIRECT_URL` + `BASE_PATH`), **includes instance path** (e.g., `https://toolkit.example.com/instance2`) |
| `UniqueID` | All | Current ticket's UniqueID (same as `this.punchlistID`) |
| `attachments` | Macros, Listeners | Pre-defined array for email attachments — only items produced by `extractUpvf`/`createPdfReport` are attached; hand-built objects are ignored |

## Execution Environment

Custom code runs server-side in a sandboxed JavaScript interpreter (Jint), not in Node.js:

- **Limits:** 30 s execution timeout, 64 MB memory, 256 call-stack depth. Exceeding one fails the
  script with a plain-language error.
- **No Node globals:** `Buffer`, `process`, `require`, `setTimeout`/`setInterval` and the rest of
  the Node API do not exist. Standard JavaScript built-ins (`JSON`, `Math`, `Date`, typed arrays,
  `Promise`, …) are available.
- **Errors are catchable:** a failing helper (`fetch` rejection, unknown field in
  `setTicketValue`, …) throws a normal JS error, so `try { … } catch (e) { … }` works as
  documented.
- **Prohibited patterns** (SQL keywords, `eval()`, `require()`, `process.`, prototype access, …)
  are rejected when a **field validator** is saved, with the pattern named in the error. Workflow
  step and listener code is checked only for its length (max 50,000 characters).

---

---

# Toolkit App for UDiTH — Toolkit Plugins

## Toolkit Plug-Ins Introduction
Toolkit supports plug-ins. These can be imported in the admin section of the frontend: 

![Importing a Plugin](media/plugin_guide_import.png)
![Plugin import UI](media/plugin_guide_import2.png)

The toolkit plugins can have frontend and backend and their own database connection. 
A sample plugin looks like this: 
![Sample Plugin in Component tree](media/plugin_guide_sample1.png)
![Sample Plugin UI](media/plugin_guide_sample2.png)


## Steps to create a plugin

The best starting is with our ready made "Hello World" example available here: 
https://github.com/caxperts/Toolkit.Plugins.Examples

The Toolkit plugin kit: the frontend SDK **and** the scripts that scaffold, validate and package a
plugin. A plugin ships as one folder with a frontend half, a `server/` .NET half, or both.


### 1. Install the SDK

The SDK is available as nuget package at: 
```
https://www.npmjs.com/package/@caxperts/toolkit-plugin-sdk
```

```bash
npm i -D @caxperts/toolkit-plugin-sdk
```

### 2. Scaffold the plugin

`toolkit-new-plugin` writes a full-stack plugin (a frontend page plus a `server/` .NET project with its
own `plg_` schema, migration and controllers) with the id already correct everywhere the host
cross-checks it. Pass `--frontend-only` for just the frontend half.

```bash
npx toolkit-new-plugin --id my_toolkit_plugin --name "My Toolkit PLugin" --dir ./my_toolkit_plugin
cd my_toolkit_plugin
```

The scaffolder prints the exact `npm install` / `validate` / `typecheck` commands to run next.

### 3. Install dependencies

Run `npm install` in the new folder. Every dependency is type-only — the build externalises the SDK,
React and DevExtreme, so the output is identical with or without them.

### 4. (Backend only) Point at the SDK feed

The scaffolded backend references one NuGet package, `CAXperts.Toolkit.Plugins.Abstractions`, so
resolve the one `nuget.config` the feed it restores from — before any backend build. Skip this
step if you scaffolded `--frontend-only`.

### 5. Declare the plugin

`src/index.tsx` calls `definePlugin({ id, name, apiVersion, routes, menu })`; the `id` must equal
`plugin.json`'s id. Everything else derives from that id — the `/api/plugins/<id>/` prefix, the CSS
scope, and the translation keys.

### 6. Build your page

Write React in `src/<Name>Page.tsx` using the host-provided hooks — `useAuth`, `useTheme`, `useT`,
`useDictionary`, `usePluginApi`, plus `http`, `toast`, `Icon` and `ErrorBoundary`. Call your own API
through `usePluginApi()` (`api.http.get('status')` → `/api/plugins/<id>/status`) rather than writing the
prefix by hand.

### 7. Style with theme variables

Write ordinary CSS in `src/styles.css`; the build prefixes every selector with `.tk-plugin-<id>` so
nothing leaks into the host. Never use a literal colour — only `var(--system-*)` / `var(--*-gradient)`,
which keeps the page readable in both dark and light themes.

### 8. (Backend only) Own your data

Give your `DbContext` its own `plg_<id>` schema and a hand-written migration, and key every row on the
OIDC `subject` (via `PluginControllerBase.TryGetSubject`) — never `ICurrentUser.Id`, which is `0` for
every real user. Put the owner in the `WHERE` clause of every read and delete so two users sharing a
table cannot see each other's rows.

### 9. Validate and typecheck

Run both before building — they need no build output, so a manifest typo or a wrong `definePlugin`
shape costs a second instead of a full build. Skipping typecheck turns a compile error into a runtime
rejection by the host's loader.

```bash
npm run validate     # manifest / menu / translations / migration
npm run typecheck    # tsc --noEmit against the SDK's types
```

### 10. Build

`npm run build` builds the frontend to `dist/`; on a full-stack plugin use `npm run build:all`, which
also runs `dotnet publish` into `backend/`.

### 11. Test locally

`npm run stage` builds and expands the plugin into `.plugins-dev/<id>/`, where the running host picks it
up. Frontend changes need a re-stage and a page reload; a backend change also needs an API restart,
because the assembly is never unloaded once loaded.

### 12. Package and install

`npm run pack` produces `<id>-<version>.zip`. An **Admin** installs it through **Settings → Plugins**
(the `POST api/settings/plugins` endpoint); the same page removes it.

## Rules the build enforces for you

These are not optional and the build/loader rejects violations, so they are worth knowing up front:

- **Don't add `react`, `react-dom` or `devextreme` as real dependencies.** The host publishes its own
  instances through an import map; a second React breaks hooks and a second DevExtreme renders a trial
  watermark. Router primitives come from `react-router` directly (the host publishes that too).
- **Four imports are build errors:** `devextreme*` directly, `react-toastify`, `react-dom/client`, and
  `react-oidc-context`. Each has a runtime symptom that looks like something else, so the build stops
  them early.
- **DevExtreme widgets** come from `@caxperts/toolkit-plugin-sdk/ui/<widget>` and data sources from
  `@caxperts/toolkit-plugin-sdk/data/<store>`, both externalised to the host's copy. To get types, add
  `devextreme@26.1.3 devextreme-react@26.1.3` as devDependencies (pinned to the host's version).
- **All user-facing text goes through `useT()`**, which resolves `{id}.{key}` → `{key}` → the key, so a
  plugin reusing a host word like `Save` gets the existing translation for free.

## Reference

- Full contract, security/stability model, database and packaging rules are available on request, 
  and so is a worked full-stack example and living style guide.
- `http` only accepts same-origin `/api/…` paths — a guard rail against leaking the bearer token, **not**
  a security boundary (plugin code runs in the host origin and can call `fetch` directly).

## License

The plugin SDK — the `@caxperts/toolkit-plugin-sdk` npm package and the
`CAXperts.Toolkit.Plugins.Abstractions` NuGet package — is released under the MIT License. The license
covers the SDK only, not the Toolkit host.

---

# UDiTH Companion — UDiTH Companion

# UDiTH Companion

UDiTH Companion is an AI assistant for the UDiTH viewer. You ask in plain language — "highlight all pumps in unit 200", "color the pipes by nominal diameter", "put a comment on every valve showing its tag" — and the Companion carries it out directly in your open UDiTH session: it queries and filters 3D objects and P&ID data, colors and highlights them, places points of interest and comments, works with sketches and layouts, and more.

## Ready to use: mcp.udith.io

**The UDiTH Companion is available as a hosted, ready-to-use application at [https://mcp.udith.io](https://mcp.udith.io).** There is nothing to install, build or host: open it inside UDiTH and start chatting. The AI model, the conversation loop and the connection to external AI tools are all run for you.

## Getting started

1. Open **https://mcp.udith.io** inside UDiTH (for example as an App Control), so the Companion runs next to your model.
2. Wait for the Companion to report that it is ready.
3. Type your request in the chat and press Enter.

The Companion works on whatever is loaded in your UDiTH session. All actions run through UDiTH's own local API in your browser.

## What it can do

- **3D model** — find, count, highlight, color, select, fit and hide/show objects by attribute conditions; read bounding boxes and attribute values; place points of interest (POIs) and comments.
- **P&ID** — list, open and close P&ID drawings; query, count, color, select and fit P&ID objects; draw lines, text and arcs on a drawing.
- **Sketches and layouts** — work with 3D and P&ID sketches and window layouts.
- **Bulk operations** — handle many elements in a single step, e.g. place a POI or a comment on every element showing one of its attributes, or color all matching elements by attribute groups, with unique colors, or on a rainbow scale of a numeric attribute.

### Contexts and workflows

- **Contexts** bundle system instructions, the set of enabled tools and the AI model for a specific task (e.g. navigation, 3D topology, federated data). Switch between them in the header, and adjust or create your own under **Settings**.
- **Workflows** guide you through multi-step tasks: each step runs with its own context, and you accept the result or keep refining before moving on.
- **Settings export/import** saves all contexts, workflows and preferences to a file and restores them — for example to share a setup with colleagues.

### Status at a glance

Two indicators in the header show whether the Companion is connected to the **AI provider** (green/red) and to the **UDiTH app** (green/red). Hover over them for details.

## Use UDiTH from your own AI tools (MCP)

mcp.udith.io also exposes your open UDiTH session to external AI clients via the **Model Context Protocol (MCP)** — for example VS Code, Claude Desktop or Cursor. Your AI client then works on your UDiTH model through the same tools as the built-in chat.

Each open Companion is its own, isolated **bridge** with a private pairing URL and token:

```
https://mcp.udith.io/mcp/<bridgeId>
Authorization: Bearer <token>
```

Find both under **Settings → MCP** in the Companion, each with a copy button. The Companion must stay open in UDiTH while your MCP client uses it — it is the bridge that executes the tool calls in your session.

**VS Code** (`.vscode/mcp.json`):

```json
{
  "servers": {
    "udith-companion": {
      "type": "http",
      "url": "https://mcp.udith.io/mcp/<bridgeId>",
      "headers": { "Authorization": "Bearer <token>" }
    }
  }
}
```

**Claude Desktop** (`claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "udith-companion": {
      "type": "streamable-http",
      "url": "https://mcp.udith.io/mcp/<bridgeId>",
      "headers": { "Authorization": "Bearer <token>" }
    }
  }
}
```

**Cursor** (`.cursor/mcp.json`):

```json
{
  "mcpServers": {
    "udith-companion": {
      "url": "https://mcp.udith.io/mcp/<bridgeId>",
      "headers": { "Authorization": "Bearer <token>" }
    }
  }
}
```

> Treat the token like a password: anyone with the URL and token can act on your open UDiTH session. Only share them with AI clients you trust.

The pairing stays the same when you reload the Companion, so you only need to configure your MCP client once per browser.

## Hosting UDiTH Companion

mcp.udith.io is the easiest way to use the Companion. If you need to run it on your own infrastructure — for example to use your own AI provider account, keep traffic inside your network, or require a company login — it is available as a Docker image.

### Docker image

```
quay.io/caxperts/companion
```

| Tag | Use |
|---|---|
| `latest` | Most recent release. |
| `2026.4.1`, `2026.4.0`, `2026.3.4`, … | A fixed release; recommended for production so updates are deliberate. |

The container serves the Companion and its API on one port (default `5000`). Open `http(s)://<your-host>:<port>` inside UDiTH instead of mcp.udith.io.

### Quick start

```sh
docker run -d --name udith-companion -p 5000:5000 \
  -e RunningLocally=false \
  -e Port=5000 \
  -e AiProvider__Provider=OpenAI \
  -e AiProvider__OpenAI__Endpoint=https://openrouter.ai/api/v1 \
  -e AiProvider__OpenAI__ApiKey=<your-api-key> \
  -e AiProvider__OpenAI__Model=anthropic/claude-sonnet-4.6 \
  quay.io/caxperts/companion:latest
```

### docker compose

```yaml
services:
  udith-companion:
    image: quay.io/caxperts/companion:latest
    ports:
      - "5000:5000"
    environment:
      RunningLocally: "false"
      Port: "5000"                     # must match the container port above
      AiProvider__Provider: OpenAI
      AiProvider__OpenAI__Endpoint: https://openrouter.ai/api/v1
      AiProvider__OpenAI__ApiKey: <your-api-key>
      AiProvider__OpenAI__Model: anthropic/claude-sonnet-4.6
      AiProvider__OpenAI__EnablePromptCaching: "false"
      # MCP server for external AI clients at http(s)://<host>/mcp/<bridgeId>
      Mcp__Enabled: "true"
      Mcp__Mode: Both                  # Passthrough | Agent | Both
      Mcp__ToolTimeoutSeconds: "60"
      # Optional company login (see "Keycloak login" below)
      Keycloak__Enabled: "false"
```

```sh
docker compose up -d
```

### Environment variables

Settings use the standard ASP.NET Core convention `Section__Key`. Variables not set fall back to the defaults built into the image, shown below.

**General**

| Variable | Required | Default | Notes |
|---|---|---|---|
| `RunningLocally` | **Yes** — set to `false` | `true` | With `true` the server only listens on `localhost` inside the container and cannot be reached. |
| `Port` | No | `5000` | Must match the container port you publish. |

**AI provider** — set `AiProvider__Provider` and the variables of that provider; the others are ignored.

| Variable | Required when | Default | Notes |
|---|---|---|---|
| `AiProvider__Provider` | Always | `OpenAI` | `OpenAI`, `AzureOpenAI`, `AzureAIInference`, `Ollama`, `Anthropic`, `Google` or `Copilot`. |
| `AiProvider__OpenAI__Endpoint` | `Provider=OpenAI` | `https://openrouter.ai/api/v1` | Any OpenAI-compatible endpoint: OpenRouter, a local llama.cpp/vLLM server, … |
| `AiProvider__OpenAI__ApiKey` | `Provider=OpenAI` | – | Any placeholder works for keyless local servers. |
| `AiProvider__OpenAI__Model` | `Provider=OpenAI` | `anthropic/claude-sonnet-4.6` | Default model; users can pick another in the Companion when the endpoint lists models. |
| `AiProvider__OpenAI__EnablePromptCaching` | Optional | `true` | Enables Anthropic prompt caching for Claude models via OpenRouter. Set `false` for endpoints that reject it. |
| `AiProvider__AzureOpenAI__Endpoint`, `__ApiKey`, `__DeploymentName` | `Provider=AzureOpenAI` | – | |
| `AiProvider__AzureAIInference__Endpoint`, `__ApiKey`, `__Model` | `Provider=AzureAIInference` | – | |
| `AiProvider__Ollama__Endpoint`, `__Model` | `Provider=Ollama` | a `localhost` endpoint | Set both: inside a container `localhost` is the container itself. No API key needed. |
| `AiProvider__Anthropic__ApiKey`, `__Model` | `Provider=Anthropic` | model `claude-sonnet-4-6` | |
| `AiProvider__Google__ApiKey`, `__Model` | `Provider=Google` | model `gemini-2.5-flash` | `AiProvider__Google__Endpoint` only for proxied setups. |
| `AiProvider__Copilot__Model` | `Provider=Copilot` | `GPT-5 mini` | Requires the GitHub Copilot CLI to be signed in on the host. Not usable with the MCP `Agent` mode. |

**MCP server** — lets external AI clients use the UDiTH session (see [Use UDiTH from your own AI tools](#use-udith-from-your-own-ai-tools-mcp)).

| Variable | Default | Notes |
|---|---|---|
| `Mcp__Enabled` | `true` | Exposes `/mcp/<bridgeId>`. |
| `Mcp__Mode` | `Both` | `Passthrough`: each UDiTH tool is an MCP tool, the client's own AI decides; no chat in the Companion. `Agent`: one tool runs the Companion's own AI. `Both`: both. |
| `Mcp__AgentToolName` | `ask_udith` | Name of the agent tool. |
| `Mcp__ToolTimeoutSeconds` | `60` | How long a tool call may take in UDiTH. |
| `Mcp__ApiKeyHashes__0`, `__1`, … | – | Without Keycloak: SHA-256 hashes of API keys for headless agents, which may then use any bridge. |
| `Mcp__AgentRole` | – | With Keycloak: realm role that lets service accounts use any bridge. |

**Keycloak login** (optional) — when enabled, only signed-in users can use the Companion, and MCP clients sign in through Keycloak instead of using a bearer token.

| Variable | Default | Notes |
|---|---|---|
| `Keycloak__Enabled` | `false` | |
| `Keycloak__Authority` | – | Realm URL, e.g. `https://auth.example.com/realms/<realm>`. |
| `Keycloak__ClientId` | – | Public client using Authorization Code + PKCE (`S256`). |
| `Keycloak__RedirectUrl` | page origin | Public URL of the Companion when it runs behind a proxy. |
| `Keycloak__Audience` | `account` | Expected `aud` claim. |
| `Keycloak__MetadataAddress`, `Keycloak__Issuer` | derived from `Authority` | Only for non-standard Keycloak setups. |

### Behind a reverse proxy / HTTPS

Run the container behind your usual reverse proxy for HTTPS. The Companion honours `X-Forwarded-Proto` and `X-Forwarded-Host`, and its chat connection falls back to Server-Sent Events or long polling when the proxy does not pass WebSockets through. External AI clients that connect from the cloud — such as Claude's custom connectors — need the server to be reachable from the internet.

## Further reading

The [API-Explorer](https://api-explorer.udith.io/) documents the UDiTH API the Companion's tools are built on.

---

# 3D SymbolDesigner — Introduction

# Introduction

**3D SymbolDesigner** enables customers to create symbols graphically for plant design software such as Intergraph® SmartPlant® 3D, without requiring programming knowledge.<br/>
**3D SymbolDesigner** is designed to support customer symbol development activities, including an interactive graphical design environment and export to a range of data formats, for example Visual Basic projects and Excel bulk-load sheets for Intergraph SmartPlant 3D.<br/>
**3D SymbolDesigner** is based on an open, scalable architecture. Symbol data and symbol variants are stored in Excel files and can be edited with Microsoft® Office or external automation software.

## Symbol examples

Piping symbol in different variants with an operator symbol.

<table>
  <thead>
    <tr>
      <th><p>In SymbolDesigner:</p></th>
      <th><p>In SmartPlant 3D:</p></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><img src="./media/2_image1.png" width="376" height="292"></td>
      <td><img src="./media/2_image2.png" width="378" height="293"></td>
    </tr>
  </tbody>
</table>

Piping symbol with an additional operator symbol.

<table>
  <thead>
    <tr>
      <th><p>In SymbolDesigner:</p></th>
      <th><p>In SmartPlant 3D:</p></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><img src="./media/2_image3.png" width="376" height="292"></td>
      <td><img src="./media/2_image4.png" width="377" height="294"></td>
    </tr>
  </tbody>
</table>

<table>
  <thead>
    <tr>
      <th><p>Piping symbol in SymbolDesigner:</p></th>
      <th><p>Operator symbol in SymbolDesigner:</p></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><img src="./media/2_image5.png" width="376" height="293"></td>
      <td><img src="./media/2_image6.png" width="376" height="293"></td>
    </tr>
  </tbody>
</table>

This symbol was exported by SymbolDesigner with the insulation aspect included:

<table>
  <tbody>
    <tr>
      <td><img src="./media/2_image7.png" width="369" height="286"></td>
      <td><img src="./media/2_image8.png" width="369" height="286"></td>
    </tr>
  </tbody>
</table>

<table>
  <thead>
    <tr>
      <th><p>Piping symbol in SymbolDesigner:</p></th>
      <th><p>Operator symbol in SymbolDesigner:</p></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><img src="./media/2_image9.png" width="376" height="292"></td>
      <td><img src="./media/2_image10.png" width="376" height="292"></td>
    </tr>
  </tbody>
</table>

<table>
  <thead>
    <tr>
      <th><p>These symbols in SmartPlant 3D:</p></th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><img src="./media/2_image11.png" width="376" height="292"></td>
      <td></td>
    </tr>
  </tbody>
</table>

<table>
  <thead>
    <tr>
      <th><p>Flow direction arrows:</p></th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><img src="./media/2_image12.png" width="377" height="293"></td>
      <td></td>
    </tr>
  </tbody>
</table>

---

# 3D SymbolDesigner — Installation

# Installation

## Setup

Requirements:

-   Supported operating systems:

	-   Microsoft Windows XP

	-   Microsoft Windows Server 2003

	-   Microsoft Windows Vista

	-   Microsoft Windows 7 or later

-   Microsoft Office XP or later (32-bit only)

-   Microsoft .NET Framework 3.5 or later

-   Microsoft Visual Basic 98 or Visual Basic 2010 or later

-   OpenGL-supporting graphics card.

	> Virtual machines may emulate OpenGL only partially and may therefore cause problems with the rendering, stability, and performance of **3D SymbolDesigner**.

Optional:

-   Intergraph Smart 3D. To compile .NET symbols, Smart 3D 2014 R1 or later is required.

Administrator rights are required on each computer to install CAXperts **3D SymbolDesigner**. **Run as** is not supported.

---

# 3D SymbolDesigner — Usage

# Usage

**3D SymbolDesigner** is a high-level symbol definition modeller. Graphical primitives can be parameterised, and dimensions, position, and rotation in space can be defined by formulas. **3D SymbolDesigner** is case-sensitive, so care is required when entering names and formulas. All symbol data are stored in XML structures, which can also be exported.

Users can also define several variants of a symbol, which can be stored in an Excel file together with the symbol geometry.

It is recommended to read the tank-with-nozzles tutorial first, even if symbols are to be built for other disciplines.

> Attributes and their interfaces are stored in `Config.xls` in the `data` subdirectory of your **3D SymbolDesigner** installation. Add parameters that are not already included at the end of the **Attributes** tab in `Config.xls`. **3D SymbolDesigner** checks the parameter mapping, that is, the last line in the table of variants, against these attributes and marks all spelling errors and missing attributes with a red background.

Sample **3D SymbolDesigner** projects can be found in the `Samples` subfolder of your **3D SymbolDesigner** program folder. The following projects are included:

<table>
  <thead>
    <tr>
      <th><strong>Project</strong></th>
      <th><strong>Discipline</strong></th>
      <th><strong>Level</strong></th>
      <th><strong>Remarks</strong></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Angle</td>
      <td>Piping (spec)</td>
      <td>***</td>
      <td>Includes manually created bulkload sheet examples.</td>
    </tr>
    <tr>
      <td>Compensator</td>
      <td>Piping (spec)</td>
      <td>****</td>
      <td>No bulkload sheets available.</td>
    </tr>
    <tr>
      <td>CustomInstrument</td>
      <td>Piping (custom instruments)</td>
      <td>**</td>
      <td>Sample bulkload sheets will be created by 3D SymbolDesigner (custom instrument tutorial).</td>
    </tr>
    <tr>
      <td>GateValveHandWheel</td>
      <td>Piping (spec)</td>
      <td>***</td>
      <td>Contains two projects, sample bulkload sheets included.</td>
    </tr>
    <tr>
      <td>Tank</td>
      <td>Equipment</td>
      <td>*</td>
      <td>Sample bulkload sheets will be created by 3D SymbolDesigner (basic tutorial).</td>
    </tr>
    <tr>
      <td>Tee</td>
      <td>Piping (spec)</td>
      <td>***</td>
      <td>Includes manually created bulkload sheet examples and a SymbolIcon.</td>
    </tr>
  </tbody>
</table>

---

# 3D SymbolDesigner — Tutorial 1 - Tank with nozzles

## Tutorial 1 - Tank with nozzles

This tutorial explains how to build and bulkload a storage tank symbol for **SmartPlant 3D**.

1.  Find out which parameter names you need to parameterise the symbol in **SmartPlant 3D**. Standard names may be found on the **CustomInterfaces** tab of C:\\Program
    Files\\SmartPlant\\3D\\CatalogData\\BulkLoad\\Datafiles\\Equipment.xls.
    You will find these names also on the Attributes tab of **3D SymbolDesigner's** Config.xls. ![](./media/image127.png)

2.  Open **3D SymbolDesigner**.

3.  Open a new symbol project (<img src="./media/2_image19.png" width="16" height="16"> in the **File** menu).

4.  Five parameters are required: *parVesselLength, parVesselDiameter, parSupportLength, parSupportHeight,* and *parSupportThickness*.
    First, select the **Parameters** item in the menu tree. Then press the **Parameter** button (<img src="./media/2_image26.png" width="16" height="16">) five times. This adds five new parameter items to the project tree:
    <img src="./media/image128.png" width="395" height="244">

5.  Rename the parameters: click `Parameter1` in the menu tree, press **F2** if necessary, and enter **parVesselLength**. Repeat this for `Parameter2` to `Parameter5` with **parVesselDiameter**, and so on:
    <img src="./media/image129.png" width="398" height="236">

6.  The table of variants should now look like this:
    <img src="./media/image130.png" width="750" height="69">

    Now enter the default values for the parameters in the table:
    <img src="./media/image131.png" width="750" height="70">

7.  Rename the `Default` variant and define the SmartPlant 3D parameter mappings. Select the `Parameter1` cell and click the right mouse button. In the context menu, select **Set SmartPlant 3D attribute**. Choose the correct attribute from the list, check **Occurrence attribute**, and press **Apply**.

    Add `OA:` in front of the parameter name if the parameter is an occurrence attribute, that is, an attribute that can be modified at runtime in SmartPlant 3D. Repeat this for `Parameter2` to `Parameter5`. **3D SymbolDesigner** checks the spelling.
    <img src="./media/image132.png" width="750" height="288">

8.  The parameters are now valid, and the background colour changes to green:
    <img src="./media/image133.png" width="779" height="52">

9.  Select the Project.Symbol.Parameters node and click twice on the **Add piping port (bolted preset)** toolbar icon (<img src="./media/image48.png" width="16" height="16">) to add two pipe ports and twice on the **Add piping connector** button (<img src="./media/image51.png" width="16" height="16">) to add two nozzles.

10. Click twice on the **Spherical Segment** button (<img src="./media/image134.png" width="16" height="16">), once on the **Cylinder** button (<img src="./media/image135.png" width="16" height="16">) and twice on the **Box** button
    (<img src="./media/image69.png" width="16" height="16">) to add the primitives for the tank geometry. The symbol tree should now look like:\
    <img src="./media/image136.png" width="317" height="324">

11. Uncheck, that is, hide, all primitives except `Cylinder1`, and expand the `Cylinder1` node in the menu tree:
    <img src="./media/image137.png" width="315" height="378">

12. Enter **parVesselLength** into the formula for the cylinder's length (L1).

13. Enter **parVesselDiameter** into D1.

14. Zoom out or in (<img src="./media/image21.png" width="16" height="16"> / <img src="./media/image22.png" width="16" height="16">) until the full cylinder is visible in the view:
    <img src="./media/image23.png" width="756" height="454">

15. Check `SphericalSegment1` and enter **180 deg** or **180°** in the `Rotation.Z` formula:
    The semi-sphere is now visible on the left side of the cylinder:
    <img src="./media/image24.png" width="756" height="454">

16. Enter **parVesselDiameter** into Geometry.D1 and **parVesselDiameter / 4** into Geometry.R1:\
    <img src="./media/image25.png" width="756" height="454">

17. Switch to wireframe mode (<img src="./media/image26.png" width="45" height="16">), then tick the `SphericalSegment2` node. Enter **parVesselDiameter** in `Geometry.D1`, **parVesselDiameter / 4** in `Geometry.R1`, and **parVesselLength** in `Translation.X` of `SphericalSegment2`:
    <img src="./media/image27.png" width="756" height="454">

18. Tick the Box1 and Box2 nodes and enter **parSupportThickness** into Geometry.L1 and **parSupportLength** into Geometry.L3 of both boxes.
    The supports should reach into the tank, so the formula for Geometry.L2 of both boxes is **parVesselDiameter / 4 + parSupportHeight**.

19. The formula for Translation.Z of both boxes is **- 3 \* parVesselDiameter / 8 - parSupportHeight / 2**.

20. Enter **parVesselLength** into Translation.X of Box2 to translate the box.

21. Rotate Box2 by entering **180 deg** into its Rotation Z.

22. Tick PipingConnector1 and PipingConnector2. Rotate PipingConnector1 by entering **180 deg** into its Rotation Z.

23. Enter **- parVesselDiameter / 2** into Translation.X of PipingConnector1.

24. Enter **parVesselLength + parVesselDiameter / 2** into Translation.X of PipingConnector2.

25. Set Geometry.L1 of PipingConnector1 and PipingConnector2 to **parVesselDiameter / 2**.

26. The modelling of the tank symbol is now finished:
    <img src="./media/image28.png" width="756" height="454">
    
27. Press the toolbar button (<img src="./media/image29.png" width="16" height="16">) to export the symbol to SmartPlant 3D.

28. Select the parent node of the new symbol in the catalogue tree view on the first tab. If you see only a node named `Catalogue`, double-click it to open the tree. Then press **Add**:
    <img src="./media/image30.png" width="230" height="118">

29. Select the new node and enter the name in the **Catalogue node** column and the description of the new symbol class in the **Key** column, for example `HorizontalTank`:
    <img src="./media/image31.png" width="600" height="670">

30. Keep this item selected and click on the **Build** tab:\
    <img src="./media/image32.png" width="600" height="389">

31. Click **Browse...** to select a target folder for the Visual Basic project and the bulkload sheet.

32. Uncheck all items on the **Compiler settings** frame.

33. When Visual Basic 6 is installed on your machine: Tick **Compile project.**

34. Click **Start**. Before the export begins, **3D SymbolDesigner** checks whether the parameters and the variant names used are valid, see *0 Naming rules*. This helps to avoid errors during the bulkload process afterwards. Any errors that occur are shown in the **Error list** tab. Clicking an item in the list moves the cursor to the corresponding cell in the table.

35. Optionally: Export of a 3D PDF datasheet (see: *Export of a 3D PDF datasheet*)

36. Press **OK** to close the **SmartPlant 3D Wizard window**. Now you may close the CAXperts **3D SymbolDesigner** program.

37. If Visual Basic or Visual Basic .NET is not available, compile the resulting Visual Basic project on a machine on which Visual Basic is installed and register the DLL on the target system.

38. Save and bulkload the Excel file by using the append mode of the **Bulkload** tool provided with SmartPlant 3D:
    <img src="./media/image83.jpg" width="588" height="715">

39. After the bulkload, the new symbol is available in the SmartPlant 3D catalogue:
    <img src="./media/image34.png" width="750" height="628">

40. After placement, the symbol looks like this:
    <img src="./media/image35.png" width="748" height="550">

---

# 3D SymbolDesigner — Tutorial 2 - Simple custom instrument

## Tutorial 2 - Simple custom instrument

Please read the tank tutorial first in order to understand the basic functions of **3D SymbolDesigner**, especially how to add, rename, and edit nodes.

In this tutorial, you will build a custom instrument part with two nozzles, a handwheel, and insulation for both the body part and the nozzles.

> Piping symbols use a right-handed coordinate system with the Y-axis pointing upwards in SmartPlant 3D. There are exceptions to this rule, for example three-way valves with an integral operator.

<img src="./media/image40.png" width="750" height="543">

### Create a new project

1.  Click (<img src="./media/2_image19.png" width="16" height="16">) to create a new symbol project.

2.  Change the coordinate system orientation by pressing the **Y-Axis up** button (<img src="./media/image112.png" width="16" height="16">).

### Add parameters

3.  Select the Parameters item in the menu tree. Then click five times on the parameter button (<img src="./media/image82.png" width="16" height="16">).

<img src="./media/image42.png" width="410" height="253">

4.  Rename the parameters: click `Parameter1`, press **F2** if needed, and enter **parFacetoFace**. Repeat this for `Parameter2` to `Parameter5` with **parActuatorHeight**, **parActuatorDiameter**, **parStemWidth**, and **parInsulationThickness**.

<img src="./media/image43.png" width="410" height="252">

5.  The table of variants should now look like:

><img src="./media/image44.png" width="750" height="92">

6.  Now enter the default values for the parameters in the table:

><img src="./media/image45.png" width="750" height="93">

7.  Rename the variant **Default** to **MyCustomInstrument**:

><img src="./media/image46.png" width="750" height="88">

8.  Rename the SmartPlant 3D parameter mappings: select the `Parameter1` cell and right-click it. In the context menu, select **Set SmartPlant 3D attribute**. Choose the correct attribute from the list, check **Occurrence attribute**, and press **Apply**. Add `OA:` in front of the parameter name if the parameter is an occurrence attribute, that is, an attribute that can be modified at runtime in SmartPlant 3D. Repeat this for `Parameter2` to `Parameter5`. **3D SymbolDesigner** checks the spelling. The parameters are now valid and the background colour changes to green:

><img src="./media/image47.png" width="750" height="51">

### Add PipingPorts

9.  Expand the **PipingPort parameter collection** toolbar icon (<img src="./media/image48.png" width="16" height="16">) and click **Piping port (bolted preset)**. One `PipingPort` will be added to the tree view and the list of variants. Repeat the step once more to add a second one.

<table>
<tr><td>

<img src="./media/image49.png" width="429" height="265">

</td><td>

<img src="./media/image50.png" width="238" height="90">

</td></tr>
</table>

### Add nozzles

10. Click twice on the button for piping connectors (<img src="./media/image51.png" width="16" height="16">). Press **F2** to rename the first nozzle node to **VNoz1** and the second to **VNoz2**.

11. Rotate **VNoz1** by 180 deg about the z axis.\
    ![](./media/image52.png)

12. Move `VNoz1` to one end of the instrument by typing the following
    into its Translation.X subnode:\
    \- parFacetoFace / 2 - PipingPort1.FlangeProjectionOrSocketOffset +
    PipingPort1.FlangeProjection +
    PipingPort1.SeatingOrGrooveOrSocketDepth.

13. Move `VNoz2` to the other end of the instrument by typing the
    following into its Translation.X subnode:\
    parFacetoFace / 2 + PipingPort2.FlangeProjectionOrSocketOffset -
    PipingPort2.FlangeProjection -
    PipingPort2.SeatingOrGrooveOrSocketDepth.

### Add graphical primitives

14. Click twice on the cone button
    (<img src="./media/image53.png" width="16" height="16">), once on sphere (<img src="./media/image54.png" width="16" height="16">), once on torus (<img src="./media/image55.png" width="16" height="16">) and four times on cylinder (<img src="./media/image135.png" width="16" height="16">).\
    <img src="./media/image56.png" width="375" height="216">

15. Rename Cone1 to **Body1**, Cone2 to **Body2**, Sphere1 to **Body3**, Torus1 to **HandWheel**, Cylinder1 to **Stem**, Cylinder2 to **InsBody**, Cylinder3 to **InsCylinderVNoz1** and Cylinder4 to
    **InsCylinderVNoz2**.\
    <img src="./media/image57.jpg" width="340" height="457">

16. Right-click the **InsBody** node, select **Properties** from the context menu, and change the aspect from **SimplePhysical** to **Insulation**. Repeat this for **InsCylinderVNoz1** and **InsCylinderVNoz2**.

    <img src="./media/image58.png" width="328" height="226">

17. Unselect all created primitives, except Body1 and Body2, using the checkbox in front of them.

18. Change to S/E isometric view as shown on the picture below.\
    <img src="./media/image138.png" width="94" height="85">

### Parameterise the primitives

19. Expand the **Body1** node and the **Geometry** subnode and type in the following formula as value for the cone's length (L1) property:\
    **parFacetoFace / 2 - PipingPort1.FlangeOrHubThickness - PipingPort1.FlangeProjection**

20. Change the value of the cone's first diameter, `D1`, in the same node to **0.001**. A distance value must not be zero, because SmartPlant 3D cannot handle zero-distance values.\
    <img src="./media/image60.png" width="337" height="144">

21. Type in the following as value for the D2 property:\ **PipingPort1.PipingOutsideDiameter**.

22. Expand the Body2 and underlying Geometry node and set the value of L1 to:\
    **parFacetoFace / 2 - PipingPort2.FlangeOrHubThickness - PipingPort2.FlangeProjection**

23. Change the value of D1 to **0.001**.

24. Set the value of the cone's second diameter (D2) to **PipingPort2.PipingOutsideDiameter**.

25. Rotate Body2 by typing **180 deg** into its Rotation.Z property.

26. Use the zoom-in tool (<img src="./media/image139.png" width="16" height="16">) until the two cones fit in the view. It should now look like this:\
    <img src="./media/image62.png" width="751" height="453">

27. Expand the Body3 and the appropriate Geometry subnode to change the diameter value (D1) to the average outside diameter of the pipe by typing:\
    **(PipingPort1.PipingOutsideDiameter + PipingPort2.PipingOutsideDiameter ) / 2**.

28. Change the L1 value of the Stem primitive to **parActuatorHeight**.

29. Set the Stem's diameter (D1) to **parStemWidth**.

30. Rotate the Stem by assigning **90 deg** to its Rotation.Z property.

31. Change the Value of the Handwheel's D1 property to **parStemWidth**.

32. Type in the formula **(parActuatorDiameter - parStemWidth) / 2** for the Handwheel's radius (R1).

33. Set its angle (A1) property to **360 deg**.

34. Expand the Handwheel's Rotation subnode and set X to **90 deg**.

35. Open the Translation branch and type in **parActuatorHeight - parStemWidth / 2** for the Y value.

36. Set Z to **-Geometry.HandWheel.Geometry.R1**.

37. Switch on the checkboxes for **Body3**, **Handwheel**, and **Stem**, then zoom out until the instrument fits in the viewport.\
    <img src="./media/image63.png" width="396" height="236">

    You should now see something like this:\
    <img src="./media/image64.png" width="231" height="295">

38. Expand InsBody and its subnode Geometry to set its length L1 to:\
    **Geometry.Body1.Geometry.L1 + Geometry.Body2.Geometry.L1**

39. Assign the formula **Max(Geometry.Body1.Geometry.D1, Geometry.Body2.Geometry.D1) + 2 \* parInsulationThickness** to its D1 property.

40. Expand the Translation subnode and set X to **-Geometry.Body1.Geometry.L1**.

41. Change to the InsCylinderVNoz1 primitive and expand its subnode Geometry. Change the value of L1 to **PipingPort1.FlangeOrHubThickness + parInsulationThickness**.

42. Type the formula **PipingPort1.FlangeOrHubOutsideDiameter + 2 \* parInsulationThickness** in D1.

43. Move this cylinder by changing its Translation.X property to **-parFacetoFace / 2 + PipingPort1.FlangeProjection**.

44. Expand the branch InsCylinderVNoz2 and the underlying Geometry node. Assign the formula **PipingPort2.FlangeOrHubThickness + parInsulationThickness** to the L1 value.

45. Set **PipingPort2.FlangeOrHubOutsideDiameter + 2 \* parInsulationThickness** for the D1 property.

46. Move the cylinder by setting **Geometry.Body2.Geometry.L1 - parInsulationThickness** for X.

47. Tick all the remaining unchecked primitives. Now the viewport should look like this:

    <img src="./media/image65.png" width="366" height="409">

### Export to SmartPlant 3D 

48. The symbol is now ready to be exported to SmartPlant 3D. Save it first, and then click the toolbar button (<img src="./media/image29.png" width="16" height="16">) to export it.

49. Optionally, press the **Reload** button. The catalogue structure is then loaded from the SP3D database. This works only if a valid SP3D database server is available and configured, see item 52.

50. Add a new node below the `Catalogue-Piping-Custom Instruments` node, select it, enter a suitable key and description, and then select it again.

51. On the second tab, **Build**, choose your target directory. You may also tick the **Compile project** checkbox under **Compiler settings**. This works only if the required SP3D libraries (`.dll`) are registered and Visual Basic 6 is installed on this machine.

52. Click the **Start** button. Before the export begins, **3D SymbolDesigner** checks whether the parameters and the variant names used are valid, see *Naming rules*. This helps to avoid errors during the bulkload process afterwards. Any errors that occur are shown in the **Error list** tab. Clicking an item in the list moves the cursor to the corresponding cell in the table.

53. Optionally: Export of a **3D PDF datasheet** (see: *Export of a 3D PDF datasheet*).

54. Start the Bulkload tool and load the DLL. In most cases, the option *Append to existing catalog* must be selected for this purpose.\
    <img src="./media/image34.jpg" width="640" height="720">

55. Check the log file for critical errors.

> The warning *Failed to set the SymbolIcon property* is only an informative message. It does not affect the usability of the symbol.

56. Now you are able to use the symbol in SmartPlant 3D.

<img src="./media/image67.png" width="616" height="324">

<img src="./media/image40.png" width="750" height="543">

---

# 3D SymbolDesigner — Tutorial 3 - Piping spec part

## Tutorial 3 - Piping spec part

Start with the symbol project created in the second tutorial.

1.  Change all parameter mappings from occurrence (`OA`) to standard mappings. To do this, remove the `OA:` prefix from all parameter mappings, except for `InsulationThickness`.

2.  Click the **SP3D Export** button (<img src="./media/image29.png" width="16" height="16">) on the toolbar.

3.  Select the **Piping** branch. This produces a piping DLL.

4.  Choose your target directory on the second tab, **Build**, and adjust the database settings on the third tab, **Bulkload**.

5.  Click **Start**. The project files are written to the target directory.

<img src="./media/image141.png" width="140" height="136">

6.  The bulkload sheets for these spec parts must be created manually. Examples of such bulkload sheets can be found in the **3D SymbolDesigner** sample folder.

    Operator symbols can be created in the same way. For these symbols, `(0, 0, 0)` is the construction base. Do not include any nozzles or ports. The export target is also **Piping**.

    To place operators in spec parts, add the operator placeholder to the base parts. In addition, add `OA:IJSecOperOrient::Rotation` to the attributes of this symbol. See the `CAXGateValve` and `CAXHandwheel` projects in the sample folder.

<img src="./media/image142.png" width="746" height="372">

7.  Bulkload the part as described in Tutorial 2, item 54 and the steps that follow.

---

# 3D SymbolDesigner — On-the-fly and stock parts

## On-the-fly and stock parts

1.  On-the-fly parts require ports that can be modified by the user after placement. The second tutorial, see above, shows how to create a piping instrument with these ports by adding a new subnode to the **Custom Instruments** or **Custom Specialties** node.

![](./media/image70.png)

2.  All **non**-on-the-fly parts, that is, stock parts, must be exported by selecting the **Piping** node directly. This applies to typical spec parts, operators, clamps, stock instrument class data, and stock piping speciality class data. See, for example, the existing data in the `InstrumentClassData` and `PipingSpecialtyClassData` sheets.

![](./media/image71.png)

> You must adapt the generated bulkload workbook after export from **3D SymbolDesigner**. For example, `PipeComponentClass` must be replaced with `SpecialtyClass` in the header of the part class sheet.

---

# 3D SymbolDesigner — Usages

# Usages

<img src="./media/image72.png" width="752" height="477">

## File

### New

**New** (<img src="./media/2_image19.png" width="16" height="16">) creates a new **3D SymbolDesigner** symbol project.

### Open

**3D SymbolDesigner** supports project files in the following formats: `.xlsx` and `.xls`, which are Microsoft Excel files containing symbol geometry data and variants, and `.xml`, which contains **3D SymbolDesigner** XML symbol geometry data.

### Save

**Save** (<img src="./media/image73.png" width="16" height="16">) saves the content of the active form.
If the file name is unknown, a **Save As** dialogue is displayed.

### Save As

**Save As** opens a **Save As** dialogue where you can choose the filename to save the content of the active form to.

### Export to SmartPlant 3D

**Export to SmartPlant 3D** (<img src="./media/image29.png" width="16" height="16">) displays a configuration dialogue (SmartPlant 3D Wizard) for exporting to Intergraph SmartPlant 3D.

The dialogue presents two tabs for configuring the export to SmartPlant 3D.

Under ideal conditions, the wizard creates a Visual Basic project containing the symbol geometry, compiles it, registers the DLL, creates an Excel file for the catalogue bulk load, and starts the bulk load into the catalogue.

The **Symbol** tab provides a catalogue tree so that you can select the target position in the SmartPlant 3D catalogue.

<table>
<tr><th>Discipline</th><th></th><th>Bulkload sheet</th></tr>
<tr><td>

Equipment

</td><td>

Add/select a folder below Equipment. Add/select a node below this folder.

</td><td>

<img src="./media/Approve.jpg" width="16" height="16">

</td></tr>
<tr><td>

Equipment component

</td><td>

Add/select a folder below Equipment Components. Add/select a node below this folder.

</td><td>

<img src="./media/Approve.jpg" width="16" height="16">

</td></tr>
<tr><td>

Piping spec symbol/standard instrument/standard specialty

</td><td>

Select Piping

</td><td>

-

</td></tr>
<tr><td>

Piping custom instrument (on-the-fly)

</td><td>

Add/select a node below Piping-Custom Instruments.

</td><td>

<img src="./media/Approve.jpg" width="16" height="16">

</td></tr>
<tr><td>

Piping custom specialty (on-the-fly)

</td><td>

Add/select a node below Piping-Custom Specialties.

</td><td>

<img src="./media/Approve.jpg" width="16" height="16">

</td></tr>
<tr><td>

Hangers and supports

</td><td>

Select Supports

</td><td>

-

</td></tr>
<tr><td>

HVAC component

</td><td>

Add/select a folder below Duct Add/select a node below this folder.

</td><td>

<img src="./media/Approve.jpg" width="16" height="16">

</td></tr>
</table>

When the **Piping** or **Supports** node is selected, **3D SymbolDesigner** will create a Visual Basic project/DLL for a piping symbol (but no Excel bulk load file).

> A file called `ComponentTree.xml` is parsed for this dialogue. It is generated by the **Reload** button and can also be modified manually to represent the catalogue tree of the target system. The third column shows the `PartClassType` of the symbol. If no type is selected, **3D SymbolDesigner** assumes that the type is *PipeComponentClass*.

The **Write full hierarchy** checkbox allows the user, in the case of a re-export, to export the full R-Hierarchy in the bulkload file. Otherwise, a re-export produces an empty R-Hierarchy sheet.

The **Build** tab shows the project settings from the project tree view and allows configuration of the Visual Basic code output and compilation.

- **Error logging**: the log file is written here.
- **Project target folder**: the Visual Basic code is written here.
- **Output format**: `VB6` code or `VB.NET`.
- **Make DLL binary compatible**: Recommended when the Visual Basic code is not modified manually by the user.
- **Include advanced debugging code**: Recommended to be disabled, as it produces debugging code in the Visual Basic project that may slow down your system.
- **Compile project**: Works only when Visual Basic 6.0 is installed and all Intergraph DLLs normally required for SmartPlant 3D symbol development are available on the system. Starts Visual Basic and compiles the Visual Basic project.

> To compile the Visual Basic 6 code, the SmartPlant 3D `Programming Resources` must be installed.

- **Delete Visual Basic files after compiler run**: cleans up the target directory.

The **Start** button executes the workflow defined in the **Symbol** and **Build** tabs. Before the export starts, **3D SymbolDesigner** checks whether the parameters and the variant names used are valid, see *0 Naming rules*. This helps to prevent errors during the subsequent bulk load process. Any errors are displayed in the **Error list** tab. Click an item in the list to move the cursor to the corresponding cell in the table.

#### Distributing symbols

If the symbol being distributed is an existing symbol that has been modified, the major version number in the Visual Basic project properties must be increased by `1`. Increasing the major version number by `1` forces the recomputation of existing symbol occurrences when the **Synchronise Model With Catalogue** command is run in Project Management. If an existing symbol is modified and distributed, all new symbol occurrences use the new symbol, unless the new occurrence uses an existing entry from the symbol cache. If an existing symbol is modified and distributed, and an existing occurrence is recomputed, it uses the new symbol if the recomputation results in the creation of a new entry in the symbol cache.

##### Preferred method

1. Place the DLL for the new or modified symbol on the server's symbols share and publish the symbol with the Update Custom Symbol Configuration command in Project Management.
1. 
##### Non-preferred method

1. On a client machine, copy the DLL from the server to the local `[Product Directory]\CatalogData\Symbols\bin` folder.
2. Register the new `.DLL` by clicking **Start > Run** and typing: `regsvr32 "[Product Directory]\CatalogData\Symbols\bin\<name of DLL>"`
3. Repeat steps 2 and 3 on each client machine.

### Recent documents

The most recently used files are listed here.

### About CAXperts 3D SymbolDesigner

![](./media/image79.png)

The **About** option displays a dialogue with information about the software product and version. The **System Info** button launches Microsoft System Information, which provides useful additional information about the hardware and software configuration of your computer system.

### Exit

**Exit** closes CAXperts **3D SymbolDesigner**.

## Project

### Variants

A variant is a named group of parameter values which can be used for testing the parameterisation of a symbol. Variants are exported to SmartPlant 3D bulk load sheets.

### Insert variant

**Insert variant** (![InsertVariant](./media/image80.png)) inserts a variant into the symbol project.

### Delete variant

**Delete variant** (![](./media/image81.png)) deletes the selected variant from the symbol project.

### Parameters

#### Add parameter

A **parameter** (![Parameter](./media/image82.png)) is an element of the symbol tree that comprises a name and a value. The name is unique across the whole symbol project and can be referenced by any formula. A parameter is mapped to a SmartPlant 3D parameter name by the SmartPlant 3D mapping row in the variants grid of the symbol project. If a lookup in `Config.xls`, located in the `data` subdirectory of **3D SymbolDesigner**, shows that this parameter name is present, the parameter mapping has a green background. Otherwise, it remains red, which means that this parameter is not exported to SmartPlant 3D. You can also change the mapping by using the context menu, right-click.

#### Add text parameter

A **text parameter** (![Text](./media/image83.png)) is an element of the symbol tree that comprises a name and a text value. The name is unique across the whole symbol project and can be referenced by any formula. A text parameter is mapped to a SmartPlant 3D parameter name by the SmartPlant 3D mapping row in the variants grid of the symbol project. Text parameters can contain non-numeric values.

### Bodies

**3D SymbolDesigner** contains the full set of shapes known from Intergraph PDS.

The following rules define how geometric transformations are applied to a shape:

1. The shape is placed at the origin of the world coordinate system, that is, the local coordinate system of the shape matches the world coordinate system, marked red-green-blue.
2. The shape's geometry is scaled according to the values set in its **.Geometry** sub-tree.
3. The shape is rotated about the global X axis by the angle specified in its **.Rotation.X** item.
4. The shape is rotated about the global Y axis by the angle specified in its **.Rotation.Y** item.
5. The shape is rotated about the global Z axis by the angle specified in its **.Rotation.Z** item.
6. The shape is translated by the distances specified in its **.Translation** subtree.

The shapes included with **3D SymbolDesigner** are described below. The anchor of each shape is marked magenta-yellow-cyan.

#### Cylinder

<img src="./media/2_image29.png" width="888" height="393">

<table>
<tr><th>3D SymbolDesigner</th><th>PDS</th><th>Comment</th></tr>
<tr><td>

Cylinder.

</td><td>

Right Circular Cylinder (1)

</td><td>

</td></tr>
<tr><td>

.Translation.X

</td><td>

X

</td><td>

</td></tr>
<tr><td>

.Translation.Y

</td><td>

Y

</td><td>

</td></tr>
<tr><td>

.Translation.Z

</td><td>

EL

</td><td>

</td></tr>
<tr><td>

.Geometry.L1

</td><td>

A

</td><td>

</td></tr>
<tr><td>

.Geometry.D1

</td><td>

B

</td><td>

</td></tr>
</table>

#### Cone

<img src="./media/2_image30.png" width="888" height="328">

<table>
<tr><th>3D SymbolDesigner</th><th>PDS</th><th>Comment</th></tr>
<tr><td>

Cone.

</td><td>

Right Circular Cone (2)

</td><td>

</td></tr>
<tr><td>

.Translation.X

</td><td>

X

</td><td>

</td></tr>
<tr><td>

.Translation.Y

</td><td>

Y

</td><td>

</td></tr>
<tr><td>

.Translation.Z

</td><td>

EL

</td><td>

</td></tr>
<tr><td>

.Geometry.L1

</td><td>

A

</td><td>

</td></tr>
<tr><td>

.Geometry.D1

</td><td>

B

</td><td>

</td></tr>
<tr><td>

.Geometry.D2

</td><td>

C

</td><td>

</td></tr>
</table>

#### Eccentric circular cone

<img src="./media/2_image31.png" width="888" height="300">

<table>
<tr><th>3D SymbolDesigner</th><th>PDS</th><th>Comment</th></tr>
<tr><td>

EccentricCircularCone.

</td><td>

Eccentric Circular Cone (3)

</td><td>

</td></tr>
<tr><td>

.Translation.X

</td><td>

X

</td><td>

</td></tr>
<tr><td>

.Translation.Y

</td><td>

Y

</td><td>

</td></tr>
<tr><td>

.Translation.Z

</td><td>

EL

</td><td>

</td></tr>
<tr><td>

.Geometry.A1

</td><td>

</td><td>

Angle of the left face

</td></tr>
<tr><td>

.Geometry.A2

</td><td>

</td><td>

Angle of the right face

</td></tr>
<tr><td>

.Geometry.AR1

</td><td>

</td><td>

Aspect ratio of the left face

</td></tr>
<tr><td>

.Geometry.AR2

</td><td>

</td><td>

Aspect ratio of the right face

</td></tr>
<tr><td>

.Geometry.D1

</td><td>

B

</td><td>

</td></tr>
<tr><td>

.Geometry.D2

</td><td>

C

</td><td>

</td></tr>
<tr><td>

.Geometry.L1

</td><td>

A

</td><td>

</td></tr>
<tr><td>

.Geometry.L2

</td><td>

</td><td>

Horizontal offset of the right face

</td></tr>
<tr><td>

.Geometry.L3

</td><td>

</td><td>

Vertical offset of the right face

</td></tr>
</table>

#### Truncated rectangular prism

<img src="./media/2_image32.png" width="888" height="382">

<table>
<tr><th>3D SymbolDesigner</th><th>PDS</th><th>Comment</th></tr>
<tr><td>

TruncatedRectangularPrism.

</td><td>

Truncated Rectangular Prism (12)

</td><td>

</td></tr>
<tr><td>

.Translation.X

</td><td>

X

</td><td>

</td></tr>
<tr><td>

.Translation.Y

</td><td>

Y

</td><td>

</td></tr>
<tr><td>

.Translation.Z

</td><td>

EL

</td><td>

</td></tr>
<tr><td>

.Geometry.L1

</td><td>

A

</td><td>

</td></tr>
<tr><td>

.Geometry.L2

</td><td>

B

</td><td>

</td></tr>
<tr><td>

.Geometry.L3

</td><td>

C

</td><td>

</td></tr>
<tr><td>

.Geometry.L4

</td><td>

D

</td><td>

</td></tr>
<tr><td>

.Geometry.L5

</td><td>

E

</td><td>

</td></tr>
</table>

#### Eccentric rectangular prism

<img src="./media/2_image33.png" width="888" height="347">

<table>
<tr><th>3D SymbolDesigner</th><th>PDS</th><th>Comment</th></tr>
<tr><td>

EccentricRectangularPrism.

</td><td>

Eccentric Rectangular Prism (13)

</td><td>

</td></tr>
<tr><td>

.Translation.X

</td><td>

X

</td><td>

</td></tr>
<tr><td>

.Translation.Y

</td><td>

Y

</td><td>

</td></tr>
<tr><td>

.Translation.Z

</td><td>

EL

</td><td>

</td></tr>
<tr><td>

.Geometry.A1

</td><td>

</td><td>

Angle of the left face

</td></tr>
<tr><td>

.Geometry.A2

</td><td>

</td><td>

Angle of the right face

</td></tr>
<tr><td>

.Geometry.L1

</td><td>

A

</td><td>

</td></tr>
<tr><td>

.Geometry.L2

</td><td>

B

</td><td>

</td></tr>
<tr><td>

.Geometry.L3

</td><td>

C

</td><td>

</td></tr>
<tr><td>

.Geometry.L4

</td><td>

D

</td><td>

</td></tr>
<tr><td>

.Geometry.L5

</td><td>

E

</td><td>

</td></tr>
</table>

#### Octagonal solid

<img src="./media/2_image34.png" width="888" height="386">

<table>
<tr><th>3D SymbolDesigner</th><th>PDS</th><th>Comment</th></tr>
<tr><td>

OctagonalSolid.

</td><td>

Octagonal Solid (8)

</td><td>

</td></tr>
<tr><td>

.Translation.X

</td><td>

X

</td><td>

</td></tr>
<tr><td>

.Translation.Y

</td><td>

Y

</td><td>

</td></tr>
<tr><td>

.Translation.Z

</td><td>

EL

</td><td>

</td></tr>
<tr><td>

.Geometry.L1

</td><td>

A

</td><td>

</td></tr>
<tr><td>

.Geometry.L2

</td><td>

B

</td><td>

Ignored

</td></tr>
<tr><td>

.Geometry.L3

</td><td>

C

</td><td>

</td></tr>
<tr><td>

.Geometry.L4

</td><td>

D

</td><td>

</td></tr>
</table>

#### Hexagonal solid

<img src="./media/2_image35.png" width="888" height="296">

<table>
<tr><th>3D SymbolDesigner</th><th>PDS</th><th>Comment</th></tr>
<tr><td>

HexagonalSolid.

</td><td>

Hexagonal Solid (7)

</td><td>

</td></tr>
<tr><td>

.Translation.X

</td><td>

X

</td><td>

</td></tr>
<tr><td>

.Translation.Y

</td><td>

Y

</td><td>

</td></tr>
<tr><td>

.Translation.Z

</td><td>

EL

</td><td>

</td></tr>
<tr><td>

.Geometry.L1

</td><td>

A

</td><td>

</td></tr>
<tr><td>

.Geometry.L2

</td><td>

B

</td><td>

Ignored

</td></tr>
<tr><td>

.Geometry.L3

</td><td>

C

</td><td>

</td></tr>
<tr><td>

.Geometry.L4

</td><td>

D

</td><td>

</td></tr>
</table>

#### Box

<img src="./media/2_image36.png" width="888" height="460">

<table>
<tr><th>3D SymbolDesigner</th><th>PDS</th><th>Comment</th></tr>
<tr><td>

Box.

</td><td>

Rectangular Solid (6)

</td><td>

</td></tr>
<tr><td>

.Translation.X

</td><td>

X

</td><td>

</td></tr>
<tr><td>

.Translation.Y

</td><td>

Y

</td><td>

</td></tr>
<tr><td>

.Translation.Z

</td><td>

EL

</td><td>

</td></tr>
<tr><td>

.Geometry.L1

</td><td>

A

</td><td>

</td></tr>
<tr><td>

.Geometry.L2

</td><td>

B

</td><td>

</td></tr>
<tr><td>

.Geometry.L3

</td><td>

C

</td><td>

</td></tr>
</table>

#### Triangular solid

<img src="./media/2_image37.png" width="888" height="361">

<table>
<tr><th>3D SymbolDesigner</th><th>PDS</th><th>Comment</th></tr>
<tr><td>

TriangularSolid.

</td><td>

Triangular Solid (5)

</td><td>

</td></tr>
<tr><td>

.Translation.X

</td><td>

X

</td><td>

</td></tr>
<tr><td>

.Translation.Y

</td><td>

Y

</td><td>

</td></tr>
<tr><td>

.Translation.Z

</td><td>

EL

</td><td>

</td></tr>
<tr><td>

.Geometry.L1

</td><td>

A

</td><td>

</td></tr>
<tr><td>

.Geometry.L2

</td><td>

B

</td><td>

</td></tr>
<tr><td>

.Geometry.L3

</td><td>

C

</td><td>

</td></tr>
<tr><td>

.Geometry.A1

</td><td>

D

</td><td>

Ignored

</td></tr>
</table>

#### Extruded solid

<img src="./media/2_image38.png" width="888" height="260">

Additional UV points can be added by **Insert → Vertices → UV point**

<table>
<tr><th>3D SymbolDesigner</th><th>PDS</th><th>Comment</th></tr>
<tr><td>

ExtrudedSolid.

</td><td>

User Projected Shape (9)

</td><td>

</td></tr>
<tr><td>

.Translation.X

</td><td>

X

</td><td>

</td></tr>
<tr><td>

.Translation.Y

</td><td>

Y

</td><td>

</td></tr>
<tr><td>

.Translation.Z

</td><td>

EL

</td><td>

</td></tr>
<tr><td>

.Geometry.L1

</td><td>

Projection

</td><td>

</td></tr>
<tr><td>

.Geometry.Vertices.UVVector1.U

</td><td>

1 X

</td><td>

</td></tr>
<tr><td>

.Geometry.Vertices.UVVector1.V

</td><td>

1 Y

</td><td>

</td></tr>
<tr><td>

...

</td><td>

...

</td><td>

</td></tr>
<tr><td>

.Geometry.Vertices.UVVector20.U

</td><td>

20 X

</td><td>

</td></tr>
<tr><td>

.Geometry.Vertices.UVVector20.V

</td><td>

20 Y

</td><td>

</td></tr>
</table>

#### Sphere

<img src="./media/2_image39.png" width="888" height="290">

<table>
<tr><th>3D SymbolDesigner</th><th>PDS</th><th>Comment</th></tr>
<tr><td>

Sphere.

</td><td>

Sphere (17)

</td><td>

</td></tr>
<tr><td>

.Translation.X

</td><td>

X

</td><td>

</td></tr>
<tr><td>

.Translation.Y

</td><td>

Y

</td><td>

</td></tr>
<tr><td>

.Translation.Z

</td><td>

EL

</td><td>

</td></tr>
<tr><td>

.Geometry.D1

</td><td>

A

</td><td>

</td></tr>
</table>

#### Semisphere

<img src="./media/2_image40.png" width="888" height="292">

<table>
<tr><th>3D SymbolDesigner</th><th>PDS</th><th>Comment</th></tr>
<tr><td>

Semisphere.

</td><td>

Semi-Elliptical Head (4)

</td><td>

</td></tr>
<tr><td>

.Translation.X

</td><td>

X

</td><td>

</td></tr>
<tr><td>

.Translation.Y

</td><td>

Y

</td><td>

</td></tr>
<tr><td>

.Translation.Z

</td><td>

EL

</td><td>

</td></tr>
<tr><td>

.Geometry.D1

</td><td>

A

</td><td>

</td></tr>
<tr><td>

.Geometry.R1

</td><td>

B

</td><td>

</td></tr>
</table>

#### Rotational solid

See extruded solid. There is no similar object in PDS.

#### Transition element

<img src="./media/2_image41.png" width="888" height="423">

<table>
<tr><th>3D SymbolDesigner</th><th>PDS</th><th>Comment</th></tr>
<tr><td>

TransitionElement.

</td><td>

Transition Element (15)

</td><td>

</td></tr>
<tr><td>

.Translation.X

</td><td>

X

</td><td>

</td></tr>
<tr><td>

.Translation.Y

</td><td>

Y

</td><td>

</td></tr>
<tr><td>

.Translation.Z

</td><td>

EL

</td><td>

</td></tr>
<tr><td>

.Geometry.L1

</td><td>

A

</td><td>

</td></tr>
<tr><td>

.Geometry.L2

</td><td>

B

</td><td>

</td></tr>
<tr><td>

.Geometry.L3

</td><td>

C

</td><td>

</td></tr>
<tr><td>

.Geometry.D1

</td><td>

D

</td><td>

</td></tr>
</table>

#### Eccentric transition element

<img src="./media/2_image42.png" width="343" height="278">

<table>
<tr><th>3D SymbolDesigner</th><th>PDS</th><th>Comment</th></tr>
<tr><td>

EccentricTransitionElement.

</td><td>

Eccentric Transition Element (16)

</td><td>

</td></tr>
<tr><td>

.Translation.X

</td><td>

X

</td><td>

</td></tr>
<tr><td>

.Translation.Y

</td><td>

Y

</td><td>

</td></tr>
<tr><td>

.Translation.Z

</td><td>

EL

</td><td>

</td></tr>
<tr><td>

.Geometry.

</td><td>

A1

</td><td>

Angle of the left face

</td></tr>
<tr><td>

.Geometry.

</td><td>

A2

</td><td>

Angle of the right face

</td></tr>
<tr><td>

.Geometry.L1

</td><td>

A

</td><td>

</td></tr>
<tr><td>

.Geometry.L2

</td><td>

B

</td><td>

</td></tr>
<tr><td>

.Geometry.L3

</td><td>

C

</td><td>

</td></tr>
<tr><td>

.Geometry.

</td><td>

L4

</td><td>

1st offset of the right face

</td></tr>
<tr><td>

.Geometry.

</td><td>

L5

</td><td>

2nd offset of the right face

</td></tr>
<tr><td>

.Geometry.D1

</td><td>

D

</td><td>

</td></tr>
</table>

#### Torus

<img src="./media/2_image43.png" width="888" height="466">

<table>
<tr><th>3D SymbolDesigner</th><th>PDS</th><th>Comment</th></tr>
<tr><td>

Torus.

</td><td>

Circular Torus (10)

</td><td>

</td></tr>
<tr><td>

.Translation.X

</td><td>

X

</td><td>

</td></tr>
<tr><td>

.Translation.Y

</td><td>

Y

</td><td>

</td></tr>
<tr><td>

.Translation.Z

</td><td>

EL

</td><td>

</td></tr>
<tr><td>

.Geometry.D1

</td><td>

A

</td><td>

</td></tr>
<tr><td>

.Geometry.R1

</td><td>

B

</td><td>

</td></tr>
<tr><td>

.Geometry.A1

</td><td>

C

</td><td>

</td></tr>
</table>

#### Rectangular torus

<img src="./media/2_image44.png" width="888" height="498">

<table>
<tr><th>3D SymbolDesigner</th><th>PDS</th><th>Comment</th></tr>
<tr><td>

RectangularTorus.

</td><td>

Rectangular Torus (11)

</td><td>

</td></tr>
<tr><td>

.Translation.X

</td><td>

X

</td><td>

</td></tr>
<tr><td>

.Translation.Y

</td><td>

Y

</td><td>

</td></tr>
<tr><td>

.Translation.Z

</td><td>

EL

</td><td>

</td></tr>
<tr><td>

.Geometry.L1

</td><td>

A

</td><td>

</td></tr>
<tr><td>

.Geometry.L2

</td><td>

B

</td><td>

</td></tr>
<tr><td>

.Geometry.R1

</td><td>

C

</td><td>

</td></tr>
<tr><td>

.Geometry.A1

</td><td>

D

</td><td>

</td></tr>
</table>

#### Vessel end

<table>
<tr><th>3D SymbolDesigner</th><th>PDS</th><th>Comment</th></tr>
<tr><td>

.Geometry.D1

</td><td>

-

</td><td>

</td></tr>
<tr><td>

.Geometry.L1

</td><td>

-

</td><td>

</td></tr>
<tr><td>

.Geometry.EndType

</td><td>

-

</td><td>

DIN28011 or DIN28013

</td></tr>
</table>

### Ports & connectors

A parameter collection (![CablePort](./media/image84.png), ![CableTrayConnector](./media/image85.png), ![CableTrayPort](./media/image86.png), ![ConduitPort](./media/image87.png), ![FoundationPort](./media/image88.png), ![HangerPort](./media/image89.png), ![HVACPort](./media/image90.png)) contains parameters which are typically provided by SmartPlant 3D for the placement and dimensioning of nozzles. You can set values in this collection for testing purposes. The values are not exported; they will be provided at runtime by SmartPlant 3D. Each connector requires its own parameter collection. Parameter collections have mappings with a grey background colour.

##### Cable connector

A **Cable connector** (![CableConnector](./media/image91.png)) indicates the start position and direction of cables leading away from the symbol.

##### Cable tray connector

A **Cable tray connector** (![CableTrayConnector](./media/image85.png)) indicates the start position, direction and orientation of cable trays leading away from the symbol.

##### Conduit connector

A **Conduit connector** (![ConduitConnector](./media/image92.png)) indicates the start position and direction of conduits leading away from the symbol.

##### Piping connector

A **Piping connector** (![PipingConnector](./media/image51.png), formerly **Variable nozzle**) indicates the start position and direction of pipes leading away from the symbol.

##### Smart piping connector for datum point based nozzles on equipment

Smart piping connectors (![SmartPipingConnector](./media/image93.png)) use N1, N2, N3, N5 and OR1 to specify their position in relation to a datum point. Therefore, they can only be used within the Geometry subnode of a **DatumPoint** object and will work only on equipment symbols. The following PlacementType values are allowed:

-   Radial
-   Tangential
-   Axial
-   Skew
-   OffsetSkew

<img src="./media/image94.png" width="751" height="589">

<img src="./media/image95.png" width="752" height="572">

<img src="./media/image96.png" width="752" height="248">

##### Elbow piping connector

An **Elbow piping connector** (![ElbowPipingConnector](./media/image97.png)) indicates the start position and direction of pipes leading away from the symbol.

##### Foundation connector

A **Foundation connector** (![FoundationConnector](./media/image98.png)) indicates the start position and direction of foundation connections.

##### HVAC connector

An **HVAC connector** (![HVACConnector](./media/image99.png)) indicates the start position and direction of HVAC pipes leading away from the symbol. On the port, the following codelist numbers are available for the CrossSectionShape: 1 Rectangular, 3 Flat Oval, 4 Round.

##### Hanger connector

A **Hanger connector** (![HangerConnector](./media/image100.png)) indicates the start position and direction of support/hanger connections.

##### Datum Point

A new project already has one datum point (![DatumPoint](./media/image101.png)). To add additional ones, please use this button. Nozzles are always logically attached to DatumPoint1 unless they belong to the Geometry subnode of another datum point.

##### Operator

An **Operator** (![Operator](./media/image102.png)) indicates the position and direction of an external operator symbol.

> To easily import the parameters of a port, use the **Load node from file** function (see: **Load node from file**).

### Points & lines

**UV vectors** can be added to **Extruded solid**s (select the .Geometry.Vertices subnode). An **UV vector** is a coordinate double representing a corner of the extruded two-dimensional shape.

A **ControlPoint** can be added to equipment symbols.

### Control flow statements

#### Add FOR loop

**For loops** are useful when the user wants to build symbols such as ladders or stairs. The Parameters subnode specifies the from, to and step values of the loop. All primitives in the Geometry subnode will be placed into a "For ... Next" loop in the Visual Basic code and should therefore be renamed to a SmartPlant 3D variable output name (i.e. "\_"-ending).

### CSG operations

#### Add union

A **union** can contain other nodes in its Geometry subnode. This might be helpful for complex symbols with many primitives. It is possible to place a **union** within a **union**; for this purpose, use the drag & drop function after creating the **union**. Similarly, to place geometry bodies in the **union**, use the drag & drop function on the Union.Geometry node.

### Parts

**Configuration index calculator** (![Calculator](./media/image103.png)) opens a "clone" of the Intergraph Configuration Index Calculator.

## Edit

### Object

The Object menu item is used to rotate and translate an object that means to turn it around the axes X, Y or Z or to move it without rotating in X, Y or Z direction.

### Clipboard

### Cut

**Cut** (![](./media/image104.png)) cuts the selected item to the clipboard.

### Copy

**Copy** (![](./media/image105.png)) copies the selected item to the clipboard.

### Paste

**Paste** (![](./media/image106.png)) pastes the clipboard content to the selected item.

### Properties

### Show properties

**Show properties** shows the properties dialogue.

## Tree context menu

### Save node to file

**Save node to file** ( ) allows the user to save the geometry data of a tree node (e.g.: cylinder, sphere, union, etc.) to an xml file.

### Load node to file

**Load node to file** ( ) allows the user to load the geometry data from an xml file to the tree.

### Copy formula

**Copy formula** ( ) allows the user to copy the selected parameter formula to the clipboard. Used on a rotation or translation node instead of a parameter, the x-, y-, and z- value will be copied.

### Paste formula

**Paste formula** ( ) allows the user to paste the clipboard content to the selected parameter formula. When used on a rotation or translation node the x-, y-, and z- value will be pasted.

## View

The **View** menu is for configuring the visual appearance of the **3D SymbolDesigner** Graphical User Interface (GUI) and of the open symbol project.

### Camera

### Zoom in

If **Zoom in** (![ZoomIn](./media/image107.png)) is clicked, the distance between the camera and the object becomes shrunk (usually by factor 0.5).

### Zoom out

If **Zoom out** (![ZoomOut](./media/image108.png)) is clicked, the distance between the camera and the object becomes stretched (usually by factor 0.5)

### Translate view

You use the **Translate view** (![Translation](./media/image109.png)) tool to translate the canvas non-destructively; it does not transform the image. Select the **Translate view** tool and click-drag in the image to translate.

### Rotate view

You use the **Rotate view** (![Rotate](./media/image110.png)) tool to rotate the canvas non-destructively; it does not transform the image. Rotating the canvas can be useful for any number of reasons, including facilitating easier painting or drawing. Select the **Rotate view** tool and click-drag in the image to rotate.

### Fit

If **Fit** (![](./media/image111.png)) is clicked, the coordinate system gets placed in the middle. The distance/magnification between the camera and the object remains unchanged.

### Y-axis up

(![YUp](./media/image112.png)) sets the Y axis = up axis (not recommended for non-piping symbols).

### Z-axis up

(![ZUp](./media/image113.png)) sets the Z axis = up axis (recommended for all symbols).

### Program

**Status bar** toggles the display of the status bar.
**Grid** toggles the display.

### Renderer

The **Renderer** changes an image by means of computer programs.

### Solid

(![Solid](./media/image114.png)) represents an image as a solid object.

### Wireframe

(![Wireframe](./media/image115.png)) shows wire frame view.

### Transparency

(![RenderTransparency](./media/image116.png)) sharp transmission of light through solid objects.

### Raster

(![RenderRaster](./media/image117.png))

### Level of detail

Numeric value from 1 to 42.

### Window

The items in the **Window** menu are for arranging the open **3D SymbolDesigner** sub forms.

### Cascade

**Cascade** (![Cascade](./media/image118.png)) cascades all open **3D SymbolDesigner** sub windows.

### Tile Horizontal

**Tile Horizontal** (![TileHorizontal](./media/image119.png)) rearranges all open **3D SymbolDesigner** sub windows in rows.

### Tile Vertical

**Tile Vertical** (![TileVertical](./media/image120.png)) rearranges all open **3D SymbolDesigner** sub windows in columns.

## Options

### Configuration file

- **Open** (![](./media/image73.png)) the selected configuration file.
- **Reload** (![Rotate](./media/image110.png)) the selected configuration file (e.g. after changes in the configuration file).

> Don't forget to save the configuration file before **Reload**.

### Log file folder

The log file folder is the folder where the **3D SymbolDesigner**.log goes to. Use **Browse...** to select a folder.

### Database

The **Database** tab shows a dialogue for configuring the database connection.

## Licence

See *Setup*.

## Help

![](./media/image121.png) shows the **3D SymbolDesigner** online help.

---

# 3D SymbolDesigner — Mathematics

# Mathematics

## Angles

Angles are interpreted in radians if they are not followed by a degree sign (`°`). You can enter `PI` or `180°` for an angle of `180°`.

> If you cannot find the degree sign on your keyboard, enter **dgn** instead. It is replaced automatically by `°`.

## Intrinsic math functions

SymbolDesigner includes a parser for mathematical functions. These functions can be used in any parameter cell in the project tree view. Parameter cells are marked with the <img src="./media/image122.png" width="16" height="16"> symbol.

Example of a formula entered by the user:

`
Sin(60°) * (10 * Geometry.Cover.Geometry.L1 / 4 - 0.025 * OperatorDiameter)
`

The formula shows the reference style used for other nodes: `Geometry.Cover.Geometry.L1` refers to the value of the `Geometry.L1` subnode of a graphical primitive named *Cover*, for example a box. The parser applies the colour and font styling automatically:

<table>
  <thead>
    <tr>
      <th><strong>Formula part</strong></th>
      <th><strong>Font style:</strong></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Operators</strong></td>
      <td>Upright, blue</td>
    </tr>
    <tr>
      <td><strong>Numbers</strong></td>
      <td>Upright, black</td>
    </tr>
    <tr>
      <td><strong>Units</strong></td>
      <td>Upright, bold, black</td>
    </tr>
    <tr>
      <td><strong>Functions</strong></td>
      <td>Upright, purple</td>
    </tr>
    <tr>
      <td><strong>Variables</strong></td>
      <td>Italics, black</td>
    </tr>
  </tbody>
</table>

The parser also interprets several constants:

<table>
  <thead>
    <tr>
      <th><strong>Constant</strong></th>
      <th><strong>Value</strong></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Pi</strong></td>
      <td>3.1415...</td>
    </tr>
    <tr>
      <td><strong>None</strong></td>
      <td>0</td>
    </tr>
    <tr>
      <td><strong>Point</strong></td>
      <td>1</td>
    </tr>
    <tr>
      <td><strong>Line</strong></td>
      <td>2</td>
    </tr>
    <tr>
      <td><strong>Fill</strong></td>
      <td>3</td>
    </tr>
    <tr>
      <td><strong>Hidden</strong></td>
      <td>0</td>
    </tr>
    <tr>
      <td><strong>SimplePhysical</strong></td>
      <td>1</td>
    </tr>
    <tr>
      <td><strong>DetailPhysical</strong></td>
      <td>16</td>
    </tr>
    <tr>
      <td><strong>Insulation</strong></td>
      <td>32</td>
    </tr>
    <tr>
      <td><strong>Operation</strong></td>
      <td>64</td>
    </tr>
    <tr>
      <td><strong>Maintenance</strong></td>
      <td>128</td>
    </tr>
    <tr>
      <td><strong>ReferenceGeometry</strong></td>
      <td>256</td>
    </tr>
  </tbody>
</table>

> These constants are not necessarily constants in the mathematical sense. They should be used as placeholders for the appropriate values, which are filled in automatically by **3D SymbolDesigner**. For example, if in software A *None* means `0`, but in software B *None* means `-1`, **3D SymbolDesigner** replaces *None* correctly according to the export target.

The following predefined operators are available:

<table>
  <thead>
    <tr>
      <th><strong>Operator</strong></th>
      <th><strong>Operation</strong></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>+</strong></td>
      <td>Addition</td>
    </tr>
    <tr>
      <td><strong>-</strong></td>
      <td>Subtraction</td>
    </tr>
    <tr>
      <td><strong>*</strong></td>
      <td>Multiplication</td>
    </tr>
    <tr>
      <td><strong>/</strong></td>
      <td>Division</td>
    </tr>
    <tr>
      <td><strong>\</strong></td>
      <td>Integer Division</td>
    </tr>
    <tr>
      <td><strong>^</strong></td>
      <td>Power</td>
    </tr>
    <tr>
      <td><strong>(</strong></td>
      <td>Left bracket</td>
    </tr>
    <tr>
      <td><strong>)</strong></td>
      <td>Right bracket</td>
    </tr>
  </tbody>
</table>

The following functions are supported by the parser:

<table>
  <thead>
    <tr>
      <th><p><strong>Function</strong></p></th>
      <th><p><strong>Function name</strong></p></th>
      <th><p><strong>Description</strong></p></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><p><strong>Abs(<em>x</em>)</strong></p></td>
      <td><p>Absolute value</p></td>
      <td><p>Returns the absolute
      value of <em>x</em>.</p></td>
    </tr>
    <tr>
      <td><p><strong>Cos(<em>x</em>)</strong></p></td>
      <td><p>Cosine trigonometric
      function</p></td>
      <td></td>
    </tr>
    <tr>
      <td><p><strong>Exp(<em>x</em>)</strong></p></td>
      <td><p>Exponential function</p></td>
      <td></td>
    </tr>
    <tr>
      <td><p><strong>IIf(<em>cond,x,y</em>)</strong></p></td>
      <td></td>
      <td><p>Returns
      <em>x</em> if condition is
      true,
      <em>y</em> if condition is
      false.
      (in Visual Basic .NET)</p></td>
    </tr>
    <tr>
      <td><p><strong>Ln(<em>x</em>)</strong></p></td>
      <td><p>Natural logarithm</p></td>
      <td></td>
    </tr>
    <tr>
      <td><p><strong>Max(<em>x,y</em>)</strong></p></td>
      <td></td>
      <td><p>Compares <em>x</em> and <em>y</em>
      and returns the greater
      operand.</p></td>
    </tr>
    <tr>
      <td><p><strong>Min(<em>x,y</em>)</strong></p></td>
      <td></td>
      <td><p>Compares <em>x</em> and <em>y</em>
      and returns the smaller
      operand.</p>
      <p>Please note: min
      (lowercase) is the unit
      sign for minutes.</p></td>
    </tr>
    <tr>
      <td><p><strong>Now(<em>x</em>)</strong></p></td>
      <td></td>
      <td><p>Returns the date (in
      Visual Basic).</p></td>
    </tr>
    <tr>
      <td><p><strong>Rnd(<em>x</em>)</strong></p></td>
      <td></td>
      <td><p>Returns a random
      number.<br>x &lt;0 The same number
      every time, using <em>x</em>
      as the seed.<br>x=0 The most recently
      generated number.<br>x&gt;0 The next random
      number in the sequence.</p></td>
    </tr>
    <tr>
      <td><p><strong>Sgn(<em>x</em>)</strong></p></td>
      <td><p>Sign function</p></td>
      <td><p>Returns the sign of <em>x</em>.</p></td>
    </tr>
    <tr>
      <td><p><strong>Sin(<em>x</em>)</strong></p></td>
      <td><p>Sine trigonometric
      function</p></td>
      <td><p>Returns the sine of <em>x</em>
      (in radians).</p></td>
    </tr>
    <tr>
      <td><p><strong>Sqrt(<em>x</em>)</strong></p></td>
      <td><p>Square root</p></td>
      <td><p>Returns the square root
      of <em>x</em>.</p></td>
    </tr>
    <tr>
      <td><p><strong>Tan(<em>x</em>)</strong></p></td>
      <td><p>Tangent trigonometric
      function</p></td>
      <td><p>Returns the tangent of
      <em>x</em>.</p></td>
    </tr>
    <tr>
      <td><p><strong>Time</strong></p></td>
      <td></td>
      <td><p>Returns the seconds
      since midnight (in 3D
      SymbolDesigner).<br>Returns the time (in
      Visual Basic)</p></td>
    </tr>
    <tr>
      <td><p><strong>Truncate(<em>x</em>)</strong></p></td>
      <td></td>
      <td><p>Returns the integer
      portion of the operand.</p></td>
    </tr>
    <tr>
      <td><p><strong>Asin(<em>x</em>)</strong></p></td>
      <td><p>Inverse sine
      trigonometric function</p></td>
      <td><p>Asin(<em>x</em>) = Atan(<em>x</em> /
      Sqrt (-<em>x</em> * <em>x</em> + 1))</p></td>
    </tr>
    <tr>
      <td><p><strong>Acos(<em>x</em>)</strong></p></td>
      <td><p>Inverse cosine
      trigonometric function</p></td>
      <td><p>Acos(<em>x</em>) = Atan(-<em>x</em> /
      Sqrt (-<em>x</em> * <em>x</em> + 1))</p>
      <ul>
      <li>2 * Atan(1)</li>
      </ul></td>
    </tr>
    <tr>
      <td><p><strong>Atan(<em>x</em>)</strong></p></td>
      <td><p>Inverse tangent
      trigonometric function</p></td>
      <td></td>
    </tr>
    <tr>
      <td><p><strong>Atan2(<em>y,x</em>)</strong></p></td>
      <td><p>Inverse tangent
      trigonometric function</p></td>
      <td></td>
    </tr>
  </tbody>
</table>

Units are defined in `Config.xls` in the `data` subdirectory of **3D SymbolDesigner**.

## Derived math functions

The following non-intrinsic mathematical functions can be derived from the intrinsic mathematical functions. These functions are not yet included in **3D SymbolDesigner**, but may be included in a future version of the software. Therefore, the names of these functions should be treated as reserved:

<table>
  <thead>
    <tr>
      <th><strong>Function</strong></th>
      <th><strong>Function name</strong></th>
      <th><strong>Derived equivalents</strong></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Sec(<em>x</em>)</strong></td>
      <td>Secant</td>
      <td>Sec(<em>x</em>) = 1 / Cos(<em>x</em>)</td>
    </tr>
    <tr>
      <td><strong>CoSec(<em>x</em>)</strong></td>
      <td>Cosecant</td>
      <td>CoSec(<em>x</em>) = 1 / Sin(<em>x</em>)</td>
    </tr>
    <tr>
      <td><strong>CoTan(<em>x</em>)</strong></td>
      <td>Cotangent</td>
      <td>CoTan(<em>x</em>) = 1 / Tan(<em>x</em>)</td>
    </tr>
    <tr>
      <td><strong>ArcSec(<em>x</em>)</strong></td>
      <td>Inverse Secant</td>
      <td>ArcSec(<em>x</em>) = ArcTan(<em>x</em> / Sqrt(<em>x</em> * <em>x</em> - 1)) + Sgn((<em>x</em>) -1) * (2 * ArcTan(1))</td>
    </tr>
    <tr>
      <td><strong>ArcCoSec(<em>x</em>)</strong></td>
      <td>Inverse Cosecant</td>
      <td>ArcCoSec(<em>x</em>) = ArcTan(<em>x</em> / Sqrt(<em>x</em> * <em>x</em> - 1)) + (Sgn(<em>x</em>) - 1) * (2 * ArcTan(1))</td>
    </tr>
    <tr>
      <td><strong>ArcCoTan(<em>x</em>)</strong></td>
      <td>Inverse Cotangent</td>
      <td>ArcCoTan(<em>x</em>) = ArcTan(<em>x</em>) + 2 * ArcTan(1)</td>
    </tr>
    <tr>
      <td><strong>HSin(<em>x</em>)</strong></td>
      <td>Hyperbolic Sine</td>
      <td>HSin(<em>x</em>) = (Exp(<em>x</em>) - E<em>x</em>p(-<em>x</em>)) / 2</td>
    </tr>
    <tr>
      <td><strong>HCos(<em>x</em>)</strong></td>
      <td>Hyperbolic Cosine</td>
      <td>HCos(<em>x</em>) = (E<em>x</em>p(<em>x</em>) + E<em>x</em>p(-<em>x</em>)) / 2</td>
    </tr>
    <tr>
      <td><strong>HTan(<em>x</em>)</strong></td>
      <td>Hyperbolic Tangent</td>
      <td>HTan(<em>x</em>) = (E<em>x</em>p(<em>x</em>) - E<em>x</em>p(-<em>x</em>)) / (E<em>x</em>p(<em>x</em>) + E<em>x</em>p(-<em>x</em>))</td>
    </tr>
    <tr>
      <td><strong>HSec(<em>x</em>)</strong></td>
      <td>Hyperbolic Secant</td>
      <td>HSec(<em>x</em>) = 2 / (E<em>x</em>p(<em>x</em>) + E<em>x</em>p(-<em>x</em>))</td>
    </tr>
    <tr>
      <td><strong>HCoSec(<em>x</em>)</strong></td>
      <td>Hyperbolic Cosecant</td>
      <td>HCoSec(<em>x</em>) = 2 / (E<em>x</em>p(<em>x</em>) - E<em>x</em>p(-<em>x</em>))</td>
    </tr>
    <tr>
      <td><strong>HCoTan(<em>x</em>)</strong></td>
      <td>Hyperbolic Cotangent</td>
      <td>HCoTan(<em>x</em>) = (E<em>x</em>p(<em>x</em>) + E<em>x</em>p(-<em>x</em>)) / (E<em>x</em>p(<em>x</em>) - E<em>x</em>p(-<em>x</em>))</td>
    </tr>
    <tr>
      <td><strong>HArcSin(<em>x</em>)</strong></td>
      <td>Inverse Hyperbolic Sine</td>
      <td>HArcSin(<em>x</em>) = Log(<em>x</em> + Sqrt(<em>x</em> * <em>x</em> + 1))</td>
    </tr>
    <tr>
      <td><strong>HArcCos(<em>x</em>)</strong></td>
      <td>Inverse Hyperbolic Cosine</td>
      <td>HArcCos(<em>x</em>) = Log(<em>x</em> + Sqrt(<em>x</em> * <em>x</em> - 1))</td>
    </tr>
    <tr>
      <td><strong>HArcTan(<em>x</em>)</strong></td>
      <td>Inverse Hyperbolic Tangent</td>
      <td>HArcTan(<em>x</em>) = Log((1 + <em>x</em>) / (1 - <em>x</em>)) / 2</td>
    </tr>
    <tr>
      <td><strong>HArcSec(<em>x</em>)</strong></td>
      <td>Inverse Hyperbolic Secant</td>
      <td>HArcSec(<em>x</em>) = Log((Sqrt(-<em>x</em> * <em>x</em> + 1) + 1) / <em>x</em>)</td>
    </tr>
    <tr>
      <td><strong>HArcCoSec(<em>x</em>)</strong></td>
      <td>Inverse Hyperbolic Cosecant</td>
      <td>HArcCoSec(<em>x</em>) = Log((Sgn(<em>x</em>) * Sqrt(<em>x</em> * <em>x</em> + 1) +1) / <em>x</em>)</td>
    </tr>
    <tr>
      <td><strong>HArcCoTan(<em>x</em>)</strong></td>
      <td>Inverse Hyperbolic Cotangent</td>
      <td>HArcCoTan(<em>x</em>) = Log((<em>x</em> + 1) / (<em>x</em> - 1)) / 2</td>
    </tr>
    <tr>
      <td></td>
      <td>Logarithm to base N</td>
      <td>LogN(<em>x</em>) = Log(<em>x</em>) / Log(N)</td>
    </tr>
  </tbody>
</table>

## Naming rules

There are some basic rules for naming parameters and objects:

1.  Within the tree view, any combination of Latin characters and numbers may be used for naming parameters and objects. A parameter name must not be a number, physical unit, or reserved word, and it cannot contain `-`, `(`, `)`, `*`, `/`, `^`, `&`, or `+`. Reserved words are listed in `Config.xls` in the **ReservedWords** worksheet.

2.  Parameter mapping names must comply with the limitations of Visual Basic, for example no reserved words and no names starting with numbers, and with the limitations of the database used by SmartPlant 3D, for example name length. All parameter mapping names used must be unique.

3.  Parameter mapping names must be chosen carefully, as they are loaded into the catalogue and remain there. Ask the SmartPlant 3D catalogue administrator in your company which parameter names should be used.

4.  Variant names must be shorter than 19 characters, must not be reserved words, and must not use the value `Default` or an empty value. Variant names must be unique and must not be identical to the class name.

---

# 3D SymbolDesigner — VBNET symbols

## .NET symbols

1.  Create the symbol as described in the tutorials above.

2.  In **Compiler Settings**, select **Visual Basic .NET** instead of **Visual Basic**.

    Only Smart 3D 2014 R1 or later supports .NET output.

    Please note: the combination of .NET valves and .NET operators is not supported before Smart 3D 2016, due to Intergraph `TR-CP-282293`.

3.  Open the resulting .vbproj file in **Visual Studio**.

4.  If **Visual Studio** shows a message that it cannot find the referenced Intergraph DLLs:

    Double-click **My Project** in the tree view, then open the **References** tab, click **Reference Paths**, and add the path to the Intergraph DLLs there. This is usually something similar to:

    `C:\Program Files (x86)\Smart3D\Core\Container\Bin\Assemblies\Release\`

5.  If you still get error messages, it might be that the Intergraph DLLs are bound to a different version of the .NET framework. Change the target framework of your symbol project in Visual Studio, e.g. with Smart 3D 2016 Hotfix 25 you need to select .NET Framework 4.5. If you can't select the necessary framework version, you might also need a newer version of Visual Studio.

6.  Run **Build** → **Rebuild**.

7.  Copy the DLL from `.\obj\Debug` to the `SymbolShare`, for example:

    `\\sp3d10\sharedcontent\Custom_Symbols`

8.  Go to Intergraph **Project Management** and start **Update Custom Symbol Configuration** for the relevant catalogue.

9.  Bulkload and place the symbol as usual.

---

# 3D SymbolDesigner — Piping nozzles

# Piping nozzles

## Parameter value sets

Starting with version 8.0 of **3D SymbolDesigner**, value sets for piping nozzle parameters can be defined in `Config.xls`. These sets can be used as follows:

![](./media/image123.png)

The following worksheets are available for defining these sets:

### PipingPortsMale

<img src="./media/image143.png" width="550" height="217">

### PipingPortsBolted

<img src="./media/image144.png" width="720" height="267">

### PipingPortsFemale

<img src="./media/image145.png" width="720" height="238">

### PipingPortsMechanical

<img src="./media/image146.png" width="689" height="259">

### PipingPortMapping

<table>
  <thead>
    <tr>
      <th>Parameter</th>
      <th>Value</th>
      <th>CodelistValue</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>EndPreparation</td>
      <td></td>
      <td>301</td>
    </tr>
    <tr>
      <td>EndPreparation</td>
      <td>BE</td>
      <td>301</td>
    </tr>
    <tr>
      <td>EndPreparation</td>
      <td>PE</td>
      <td>391</td>
    </tr>
    <tr>
      <td>EndPreparation</td>
      <td>MTE</td>
      <td>331</td>
    </tr>
    <tr>
      <td>EndPreparation</td>
      <td>SWE</td>
      <td>421</td>
    </tr>
    <tr>
      <td>EndPreparation</td>
      <td>FTE</td>
      <td>441</td>
    </tr>
    <tr>
      <td>EndPreparation</td>
      <td>RFFE</td>
      <td>21</td>
    </tr>
    <tr>
      <td>EndPreparation</td>
      <td>RFTBE</td>
      <td>121</td>
    </tr>
    <tr>
      <td>EndPreparation</td>
      <td>FORM-B1</td>
      <td>21</td>
    </tr>
    <tr>
      <td>EndPreparation</td>
      <td>FORM-B1-TBE</td>
      <td>121</td>
    </tr>
    <tr>
      <td>EndPreparation</td>
      <td>MJBLE</td>
      <td>542</td>
    </tr>
    <tr>
      <td>EndStandard</td>
      <td>US-DEFAULT</td>
      <td>5</td>
    </tr>
    <tr>
      <td>EndStandard</td>
      <td>B16.47A</td>
      <td>945</td>
    </tr>
    <tr>
      <td>EndStandard</td>
      <td>DIN-DEFAULT</td>
      <td>100</td>
    </tr>
  </tbody>
</table>

## Smart 3D

### PipingGenericDataBolted

<table>
  <thead>
    <tr>
      <th></th>
      <th>Attribute</th>
      <th>Remark</th>
      <th>Must</th>
      <th>Sketch</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>NominalPipingDiameter</td>
      <td>Input</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>2</td>
      <td>NominalDiameterUnits</td>
      <td>Input</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>3</td>
      <td>PressureRating</td>
      <td>Input, Codelist</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>(3a)</td>
      <td>(RatingPractice)</td>
      <td>Input, Codelist PressureRating, Parent of PressureRating</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>4</td>
      <td>EndPreparation</td>
      <td>Input, Codelist</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>(4a)</td>
      <td>(TerminationSubClass)</td>
      <td>Input, Codelist EndPreparation, Parent of EndPreparation</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>(4b)</td>
      <td>(TerminationClass)</td>
      <td>Input, Codelist EndPreparation, Parent of TerminationSubClass</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>5</td>
      <td>EndStandard</td>
      <td>Input, Codelist</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>(5a)</td>
      <td>(EndPractice)</td>
      <td>Input, Codelist EndStandard, Parent of EndStandard</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>6</td>
      <td>FlangeOutsideDiameter</td>
      <td>see sketch</td>
      <td>X</td>
      <td>A</td>
    </tr>
    <tr>
      <td>7</td>
      <td>FlangeThickness</td>
      <td>see sketch</td>
      <td>(X)</td>
      <td>B</td>
    </tr>
    <tr>
      <td>8</td>
      <td>FlangeThicknessTolerance</td>
      <td>is required for computing bolt length</td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>9</td>
      <td>FlangeFaceProjection</td>
      <td>see sketch</td>
      <td></td>
      <td>C</td>
    </tr>
    <tr>
      <td>10</td>
      <td>RaisedFaceDiameter</td>
      <td>see sketch</td>
      <td></td>
      <td>D</td>
    </tr>
    <tr>
      <td>11</td>
      <td>FlangeGrooveWidth</td>
      <td>see sketch</td>
      <td></td>
      <td>E</td>
    </tr>
    <tr>
      <td>12</td>
      <td>SeatingDepth</td>
      <td>see sketch</td>
      <td></td>
      <td>F</td>
    </tr>
    <tr>
      <td>13</td>
      <td>BoltCircleDiameter</td>
      <td>see sketch</td>
      <td>(X)</td>
      <td>G</td>
    </tr>
    <tr>
      <td>14</td>
      <td>QuantityOfBoltsRequired</td>
      <td></td>
      <td>(X)</td>
      <td></td>
    </tr>
    <tr>
      <td>15</td>
      <td>BoltDiameter</td>
      <td></td>
      <td>(X)</td>
      <td></td>
    </tr>
    <tr>
      <td>16</td>
      <td>BodyOutsideDiameter</td>
      <td>see sketch</td>
      <td>(X)</td>
      <td>H</td>
    </tr>
    <tr>
      <td>17</td>
      <td>DrillingTemplatePattern</td>
      <td>Codelist</td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>18</td>
      <td>BoltPatternLength</td>
      <td>used for not circular bolted ports</td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>19</td>
      <td>BoltPatternWidth</td>
      <td>used for not circular bolted ports</td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>20</td>
      <td>GroovePitchDiameter</td>
      <td>used for ring type joint flanged ends only</td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>21</td>
      <td>LapThickness</td>
      <td>used for lap joint flange without stub end (part) only</td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>22</td>
      <td>CounterBoreDepth</td>
      <td>used for Bolt Type=&quot;Cap screw, socket head, counter bore&quot; only</td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>23</td>
      <td>BoltPatternOffset</td>
      <td>used for not circular bolted ports</td>
      <td></td>
      <td></td>
    </tr>
  </tbody>
</table>

### PipingGenericDataFemale

<table>
  <thead>
    <tr>
      <th></th>
      <th>Attribute</th>
      <th>Remark</th>
      <th>Must</th>
      <th>Sketch</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>NominalPipingDiameter</td>
      <td>Input</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>2</td>
      <td>NominalDiameterUnits</td>
      <td>Input</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>3</td>
      <td>PressureRating</td>
      <td>Input, Codelist</td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>(3a)</td>
      <td>(RatingPractice)</td>
      <td>Input, Codelist PressureRating, Parent of PressureRating</td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>4</td>
      <td>Schedule</td>
      <td>Input, Codelist ScheduleThickness</td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>(4a)</td>
      <td>(ScheduleThicknessPractice)</td>
      <td>Input, Codelist ScheduleThickness, Parent of Schedule</td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>5</td>
      <td>EndPreparation</td>
      <td>Input, Codelist</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>(5a)</td>
      <td>(TerminationSubClass)</td>
      <td>Input, Codelist EndPreparation, Parent of EndPreparation</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>(5b)</td>
      <td>(TerminationClass)</td>
      <td>Input, Codelist EndPreparation, Parent of TerminationSubClass</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>6</td>
      <td>EndStandard</td>
      <td>Input, Codelist</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>(6a)</td>
      <td>(EndPractice)</td>
      <td>Input, Codelist EndStandard, Parent of EndStandard</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>7</td>
      <td>SocketDiameter</td>
      <td>see sketch</td>
      <td>X</td>
      <td>A</td>
    </tr>
    <tr>
      <td>8</td>
      <td>SocketDepth</td>
      <td>see sketch</td>
      <td></td>
      <td>B</td>
    </tr>
    <tr>
      <td>9</td>
      <td>SocketOffset</td>
      <td>see sketch</td>
      <td></td>
      <td>C</td>
    </tr>
    <tr>
      <td>10</td>
      <td>ThreadDepth</td>
      <td>see sketch</td>
      <td></td>
      <td>D</td>
    </tr>
    <tr>
      <td>11</td>
      <td>HubOutsideDiameter</td>
      <td>see sketch</td>
      <td>(X)</td>
      <td>E</td>
    </tr>
    <tr>
      <td>12</td>
      <td>HubThickness</td>
      <td>see sketch</td>
      <td>(X)</td>
      <td>F</td>
    </tr>
    <tr>
      <td>13</td>
      <td>SocketWallThickness</td>
      <td>see sketch</td>
      <td></td>
      <td>G</td>
    </tr>
    <tr>
      <td>14</td>
      <td>BodyOutsideDiameter</td>
      <td>see sketch</td>
      <td>(X)</td>
      <td>H</td>
    </tr>
  </tbody>
</table>

### PlainPipingGenericData

<table>
  <thead>
    <tr>
      <th></th>
      <th>Attribute</th>
      <th>Remark</th>
      <th>Must</th>
      <th>Sketch</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>NominalPipingDiameter</td>
      <td>Input</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>2</td>
      <td>NominalDiameterUnits</td>
      <td>Input</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>3</td>
      <td>PressureRating</td>
      <td>Input, Codelist</td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>(3a)</td>
      <td>(RatingPractice)</td>
      <td>Input, Codelist PressureRating, Parent of PressureRating</td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>4</td>
      <td>Schedule</td>
      <td>Input, Codelist ScheduleThickness</td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>(4a)</td>
      <td>(ScheduleThicknessPractice)</td>
      <td>Input, Codelist ScheduleThickness, Parent of Schedule</td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>5</td>
      <td>EndStandard</td>
      <td>Input, Codelist</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>(5a)</td>
      <td>(EndPractice)</td>
      <td>Input, Codelist EndStandard, Parent of EndStandard</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>6</td>
      <td>PipingOutsideDiameter</td>
      <td>see sketch</td>
      <td>(X)</td>
      <td>A</td>
    </tr>
    <tr>
      <td>7</td>
      <td>WallThickness</td>
      <td>see sketch</td>
      <td></td>
      <td>B</td>
    </tr>
  </tbody>
</table>

### PipingGenericDataMechanical

<table>
  <thead>
    <tr>
      <th></th>
      <th>Attribute</th>
      <th>Remark</th>
      <th>Must</th>
      <th>Sketch</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>NominalPipingDiameter</td>
      <td>Input</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>2</td>
      <td>NominalDiameterUnits</td>
      <td>Input</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>3</td>
      <td>PressureRating</td>
      <td>Input, Codelist</td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>(3a)</td>
      <td>(RatingPractice)</td>
      <td>Input, Codelist PressureRating, Parent of PressureRating</td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>4</td>
      <td>EndPreparation</td>
      <td>Input, Codelist</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>(4a)</td>
      <td>(TerminationSubClass)</td>
      <td>Input, Codelist EndPreparation, Parent of EndPreparation</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>(4b)</td>
      <td>(TerminationClass)</td>
      <td>Input, Codelist EndPreparation, Parent of TerminationSubClass</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>5</td>
      <td>EndStandard</td>
      <td>Input, Codelist</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>(5a)</td>
      <td>(EndPractice)</td>
      <td>Input, Codelist EndStandard, Parent of EndStandard</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>6</td>
      <td>GrooveDepth</td>
      <td>see sketch</td>
      <td></td>
      <td>A</td>
    </tr>
    <tr>
      <td>7</td>
      <td>GrooveWidth</td>
      <td>see sketch</td>
      <td></td>
      <td>B</td>
    </tr>
    <tr>
      <td>8</td>
      <td>GrooveSetback</td>
      <td>see sketch</td>
      <td></td>
      <td>C</td>
    </tr>
    <tr>
      <td>(9)</td>
      <td>Body/PipingOutsideDiameter</td>
      <td>is used of PlainPipingGenericData</td>
      <td></td>
      <td>D</td>
    </tr>
  </tbody>
</table>

### Port Attributes in SymbolDesigner

<table>
  <thead>
    <tr>
      <th></th>
      <th>Attribute</th>
      <th>Remark</th>
      <th>Bolted</th>
      <th>Female</th>
      <th>Plain</th>
      <th>Mech</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>NPD</td>
      <td>Input</td>
      <td>X</td>
      <td>X</td>
      <td>X</td>
      <td>X</td>
    </tr>
    <tr>
      <td>2</td>
      <td>NPDUnitType</td>
      <td>Input</td>
      <td>X</td>
      <td>X</td>
      <td>X</td>
      <td>X</td>
    </tr>
    <tr>
      <td>3</td>
      <td>PressureRating</td>
      <td>Input, Codelist</td>
      <td>X</td>
      <td>X</td>
      <td>X</td>
      <td>X</td>
    </tr>
    <tr>
      <td>4</td>
      <td>RatingPractice</td>
      <td>Input, Codelist PressureRating, Parent of PressureRating</td>
      <td>X</td>
      <td>X</td>
      <td>X</td>
      <td>X</td>
    </tr>
    <tr>
      <td>5</td>
      <td>ScheduleThickness</td>
      <td>Input, Codelist</td>
      <td></td>
      <td>X</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>6</td>
      <td>SchedulePractice</td>
      <td>Input, Codelist ScheduleThickness, Parent of Schedule</td>
      <td></td>
      <td>X</td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>7</td>
      <td>EndPreparation</td>
      <td>Input, Codelist</td>
      <td>X</td>
      <td>X</td>
      <td></td>
      <td>X</td>
    </tr>
    <tr>
      <td>8</td>
      <td>TerminationClass</td>
      <td>Input, Codelist EndPreparation, Parent of EndPreparation</td>
      <td>X</td>
      <td>X</td>
      <td></td>
      <td>X</td>
    </tr>
    <tr>
      <td>9</td>
      <td>TerminationSubClass</td>
      <td>Input, Codelist EndPreparation, Parent of TerminationSubClass</td>
      <td>X</td>
      <td>X</td>
      <td></td>
      <td>X</td>
    </tr>
    <tr>
      <td>10</td>
      <td>EndStandard</td>
      <td>Input, Codelist</td>
      <td>X</td>
      <td>X</td>
      <td>X</td>
      <td>X</td>
    </tr>
    <tr>
      <td>11</td>
      <td>EndPractice</td>
      <td>Input, Codelist EndStandard, Parent of EndStandard</td>
      <td>X</td>
      <td>X</td>
      <td>X</td>
      <td>X</td>
    </tr>
    <tr>
      <td>12</td>
      <td>PipingPointBasis</td>
      <td>Input PartClass, Codelist PipingPointUsage</td>
      <td>X</td>
      <td>X</td>
      <td>X</td>
      <td>X</td>
    </tr>
    <tr>
      <td>13</td>
      <td>ID</td>
      <td>Input PartClass</td>
      <td>X</td>
      <td>X</td>
      <td>X</td>
      <td>X</td>
    </tr>
    <tr>
      <td>14</td>
      <td>PortIndex</td>
      <td>Input PartClass</td>
      <td>X</td>
      <td>X</td>
      <td>X</td>
      <td>X</td>
    </tr>
    <tr>
      <td>15</td>
      <td>FlowDirection</td>
      <td>Input PartClass, Codelist</td>
      <td>X</td>
      <td>X</td>
      <td>X</td>
      <td>X</td>
    </tr>
    <tr>
      <td>16</td>
      <td>FlangeOrHubOutsideDiameter</td>
      <td></td>
      <td>X</td>
      <td>X</td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>17</td>
      <td>FlangeOrHubThickness</td>
      <td></td>
      <td>X</td>
      <td>X</td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>18</td>
      <td>FlangeOrMechanicalGrooveWidth</td>
      <td></td>
      <td>X</td>
      <td></td>
      <td></td>
      <td>X</td>
    </tr>
    <tr>
      <td>19</td>
      <td>FlangeProjectionOrSocketOffset</td>
      <td></td>
      <td>X</td>
      <td>X</td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>20</td>
      <td>FlangeProjection</td>
      <td>only for &quot;Bolted&quot;</td>
      <td>X</td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>21</td>
      <td>LinerThicknessAtFaceOfFlange</td>
      <td>used according to LinerThicknessData- Sheet only</td>
      <td>(X)</td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>22</td>
      <td>LinerThicknessAtInsideDiameter</td>
      <td>used according to LinerThicknessData- Sheet only</td>
      <td></td>
      <td></td>
      <td>(X)</td>
      <td></td>
    </tr>
    <tr>
      <td>23</td>
      <td>PipingInsideDiameter</td>
      <td>calculated in SP3D (PipingOutsideDiameter - 2 * WallThickness)</td>
      <td></td>
      <td></td>
      <td>(X)</td>
      <td></td>
    </tr>
    <tr>
      <td>24</td>
      <td>PipingOutsideDiameter</td>
      <td>attribute Body/PipingOutsideDiameter of GenericData</td>
      <td>X</td>
      <td>X</td>
      <td>X</td>
      <td>(X)</td>
    </tr>
    <tr>
      <td>25</td>
      <td>RaisedFaceOrSocketDiameter</td>
      <td></td>
      <td>X</td>
      <td>X</td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>26</td>
      <td>ReinforcedWallThickness</td>
      <td>used according to DINWallThicknessData- Sheet only</td>
      <td></td>
      <td></td>
      <td>X</td>
      <td></td>
    </tr>
    <tr>
      <td>27</td>
      <td>SeatingOrGrooveOrSocketDepth</td>
      <td></td>
      <td>X</td>
      <td>X</td>
      <td></td>
      <td>X</td>
    </tr>
    <tr>
      <td>28</td>
      <td>SeatingDepth</td>
      <td>only for &quot;Bolted&quot;</td>
      <td>X</td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>29</td>
      <td>WallThicknessOrGrooveSetback</td>
      <td></td>
      <td></td>
      <td>X</td>
      <td>X</td>
      <td>X</td>
    </tr>
  </tbody>
</table>

![](./media/image124.png)

![](./media/image125.png)

---

# 3D SymbolDesigner — Custom forms

## Custom forms

Since version `07.00.1602.1704`, **3D SymbolDesigner** creates a form definition project template in the export folder of the symbol. The solution or project file can be opened, modified, and compiled with the current version of Microsoft C#.

The template lists all available attributes in text or drop-down boxes:

<img src="./media/image36.png" width="752" height="577">

Use the form editing functions in Visual Studio to modify the template, for example to replace the background image, rearrange the input boxes, or change the font size:

![](./media/image37.png)

Build the DLL in Visual Studio, place it on the symbol share, and run **Update Custom Symbol Configuration** in **Smart 3D Project Management**.

Use the **FormDefinition** attribute in the symbol bulkload file to assign the form to the symbol. If the bulkload is successful, the custom form
(![](./media/image38.png)) can be used in Smart 3D:

![](./media/image39.png)

---

# SystemFor4d — Introduction

# Introduction

CAXperts 4D System converts Excel files containing 4D/5D data (e.g. from Oracle Primavera P6 or Microsoft Project) directly into UPV animation files.


## Prerequisites

-   Supported operating systems:
    -   Microsoft Windows XP, Vista, 7, or later
    -   Microsoft Windows Server 2003 or later
-   UDiTH API
-   Microsoft Office 2003 or later

---

# SystemFor4d — Usage

# Usage

1. Place `4D System.exe` in the same folder as `UDiTH.exe`.
2. Run the executable by using a command such as the following:

    `"4D System.exe" --nowindow --convert "P6-Activities Architectural.xlsx" "P6-Activities Architectural.upvf"`

This converts the `P6-Activities Architectural.xlsx` file into a UPV animation file by using the 3D model in the `Data` folder of this UPV instance.

---

# DrawingXpert — DrawingXpert

# Introduction

**DrawingXpert** is a highly automated and flexible tool for modifying the content of SmartSketch drawings based on various rules.
The tool can process individual drawings as well as folders and subfolders.
It can be run either interactively or in command-line mode.

## Prerequisites

-   Microsoft Windows 7 or later

-   Microsoft Excel 2003 or higher

-   Intergraph SmartSketch 2014 R1 or higher

## General functions and Graphic User Interface

**DrawingXpert** has separate sections for **Input**, **Configuration**, **Coordinate Shift**, and **Output**.
When **DrawingXpert** starts, a log file is created in the default location `%temp%/DrawingXpert`.

<img src="./media/image8.png" width="789" height="833">

<table>
  <tbody>
    <tr>
      <td><img src="./media/image22.png" width="44" height="44"></td>
      <td><p>This is the area in which the input is defined. You can select either individual files or folders.
      If a folder is selected, <strong>DrawingXpert</strong> also searches all subfolders. Only files with the defined extension are processed.</p></td>
    </tr>
    <tr>
      <td><img src="./media/image23.png" width="44" height="44"></td>
      <td><p>Location of the configuration file in which the rules for modifying the drawings are defined.</p></td>
    </tr>
    <tr>
      <td><img src="./media/image24.png" width="44" height="44"></td>
      <td><p>Section in which the movement can be defined. Movement means the relative distances for the coordinate labels on a drawing.
      <strong>DrawingXpert</strong> reads and calculates the new values for the coordinate labels.</p></td>
    </tr>
    <tr>
      <td><img src="./media/image25.png" width="44" height="44"></td>
      <td><p>Output folder for the drawings after modification. There are two possible options:
      If the input folder is the same as the output folder, the drawings are overwritten with the changes. If a different output folder is defined, the modified drawings are saved there and the source files remain unchanged. In all cases, <strong>DrawingXpert</strong> creates a backup of all processed drawings.</p></td>
    </tr>
    <tr>
      <td><img src="./media/image26.png" width="44" height="44"></td>
      <td><p>The <strong>Export text to Excel</strong> button extracts all text from SmartSketch and stores it in the Excel configuration file, in the <code>ScanResult</code> and <code>ScanResultLink</code> sheets.
      If the Excel file does not exist, a new file is created automatically. If the file already exists, the existing sheets are extended with the new text.
      All extracted texts are exported only once per file in order to avoid duplicates.</p></td>
    </tr>
    <tr>
      <td><img src="./media/image27.png" width="44" height="44"></td>
      <td><p><strong>Replace in Drawings</strong> starts processing of the drawings, including text replacement and, if enabled, transformation to new coordinates.</p></td>
    </tr>
  </tbody>
</table>

## Settings

> All settings made in the UI, such as the path to the Excel configuration file and the input and output folders, are saved in a `settings.ini` file. By default, this file is saved in:
> `....user\AppData\Roaming\CAXperts\DrawingXpert\settings.ini`
>
> This settings file can also be saved to another location and loaded from there by using these options:
> <img src="./media/image9.png" width="210" height="128">

## Excel Configuration file

> This file contains a set of rules for the modification on a drawing.\
> This file is delivered with the software and contains already some
> predefined examples.\
> The default location for the Configuration is:\
> \"C:\\Program Files
> (x86)\\CAXperts\\DrawingXpert\\Config\\Config.xls\"\
> From that location you should copy that into a project specific folder
> in order to make the project specific adaptions.

### ScanResult

> With "Export text to Excel" all texts from the selected drawings are written to the tab in "Scan Result".\
> For the export of your textnodes you can either use your existing
> config-file from <img src="./media/image23.png" width="44" height="44">
> Or you can create a new Excel-Workbook just by defining a new Excel-Workbook.\
> **DrawingXpert** creates this new workbook which just contains the sheets
> "ScanResult" and
> <img src="./media/image10.png" width="642" height="19">
>
> All text nodes are grouped by drawing:
>
> <img src="./media/image11.png" width="642" height="249">
>
> Both sheets are for information only. Changes made there have no effect on the drawings.

### ScanResultLinks

> There is also a sheet for the linked documents referenced to the
> drawing.\
> The links are store in the Link-table in SmartSketch:
>
> <img src="./media/image12.png" width="491" height="284">
>
> **DrawingXpert** is able to read out these links and store this information in
> ScanResultLinks-sheet:
>
> ![](./media/image13.png)
>
> ![](./media/image14.png)
>
> **DrawingXpert** can also change these links to referenced documents.
> The replacement path for the reference file must be defined in the `FindReplace` sheet.

### FindReplace

> In that sheet you define the rules for the modifications on the
> drawings.\
> The rules are based on regular expressions.
>
> ![](./media/image15.png)
>
> A detailed explanation and description how to use these regular
> expression are available in the information
> tab:![](./media/image16.png)
>
> In the **Find** column, you define the rule that determines which string should be found.
> In the **Replace** column, you define the replacement string.
>
> All rules are executed top-down -\> more than one rule can be applied
> on the same text element. To change this behaviour "Stop flag" can be
> set. If "Stop flag" is set to 1 for a particular rule which matches
> some text element, no further rules are executed on the same element.
> This prevents multiple replacements on the same text element.
>
> Xmin (m), Ymin (m), Xmax (m), Ymax (m):\
> Here you can define an area on the drawing where the rules are
> executed. Only text elements within defined range will be processed.\
> With that you can easily define the area for the title-block or area
> for the revisions.\
> This makes sure that the specific rules do not affect other parts of
> the drawing where the rules may also fit for.

### Coordinate

> This is the Coordinate-recognition tab.\
> Here you define how a coordinate-string can be distinguished from
> other textnodes.\
> ![](./media/image17.png)
>
> According to these rules, the coordinates are categorised into the X+, X-, Y+, Y-, Z+, and Z- categories.

### ChangeCoordinate

> The coordinates are changed according to the values defined in section <img src="./media/image24.png" width="44" height="44">.
> This allows coordinate changes to be applied within the drawings.
> In the **ChangeCoordinate** tab, you define what happens when a coordinate changes, for example from West (-X) to East (+X).
> You can even change the complete meaning of the coordinate prefix or suffix.
> In this way, a change from `X -12000` to `East +24000` is possible.

### ClearBlock

> In that section you can define areas on the drawing that can be
> cleared from their content.\
> You also can define dedicated object-types with in combination with a
> specific colour.\
> This functionality is very useful to empty complete revision blocks or
> dedicated fields in a title block.
>
> <img src="./media/image28.png" width="901" height="572">

### TitleBlock

> With that functionality you can "fill" your TitleBlock fields again
> that you have deleted before with the new values.\
> Here you define the name of the single parts of a line for a
> revision:\
> e.g.\
> ![](./media/image20.png)

### TitleBlockDefinition

> In the Title Block definition you define where these cells are placed
> on the drawing together with the Font, Font size, Colour and Typewrite.
>
> ![](./media/image21.png)
>
> With that definition, you are able to create new entries on a drawing.

---

# Setup and Licensing — Standard Setup of CAXperts Software

# Standard Setup of CAXperts Software

## How to Install

This chapter explains the installation process for most CAXperts products.

> **Administrator rights** are required on each computer in order to complete the installation process.

The **Run as** functionality is not supported for installation.

1. To download CAXperts software, go to `caxperts.com`, where the software is hosted.

2. Navigate to **Downloads** in the **Support** drop-down menu.

<img src="./media/images_SetupSoftware_step1-2.png" width="500">

3. Select the desired software setup and click on the yellow **Download** button next to it.

<img src="./media/images_SetupSoftware_step3.png" width="500">

4. Find the downloaded file in your **Downloads** folder.

5. Double-click the downloaded file to open it and begin the installation process.

<img src="./media/images_SetupSoftware_step4-5.png" width="300">

6. You will then be prompted to enter your **admin username** and **password**.

<img src="./media/images_SetupSoftware_step6.png" width="300">

7. Follow the instructions provided by the setup wizard.

<img src="./media/images_SetupSoftware_step7.png" width="300">

8. Select the folder in which you want to install the software.

<img src="./media/images_SetupSoftware_step8.png" width="300">

9. Proceed with the installation by clicking the **Install** button, then wait for the installation process to complete.

<img src="./media/images_SetupSoftware_step9-10.png" width="300">

10. You can now start the installed software.

## How to Uninstall

1. To uninstall the software, open **Control Panel** from the Start menu.
2. Then navigate to **Programs and Features**.
3. Select the program you want to remove from the list, then either click **Uninstall/Remove** or right-click the program and select **Uninstall a program**.

## Command Line Setup (for Administrators)

The installer supports the following command line options:

### /S:\<\<optional INI file\>\>

Runs the installation in silent mode, where no screens or dialogs are shown.

An optional INI file can be provided in order to specify session variable values. Example:

`"C:\output\setup.exe" "/S:C:\setupvars.ini"`

In this case, the session variables in the INI file are used during setup. The INI file should use the following format:

`[SetupValues]`

`%AppFolder%=C:\Program Files\MyProduct`

### /U:\<\<XML config file\>\>

This option is used when uninstalling the program by command line. An optional XML file can be provided in order to specify session variable values. Example:

`"C:\Program Files\MyProduct\uninstall.exe" "/U:C:\Program Files\MyProduct\irunin.xml"`

---

# Setup and Licensing — Licence Installation and Upgrade

# Licence Installation

> The endpoint used during licensing is [https://api365.slascone.com:443](https://api365.slascone.com:443). A connection to `20.126.54.190` (`api365.slascone.com`) on port `443` is required.

Depending on your network configuration, you may need to add this endpoint to your firewall allow list.

If your CAXperts application does not have a valid licence key, the **Update licence** dialogue appears each time you start the application.

<img src="./media/images_image4.png" width="562" height="434">

This dialogue shows the current domain name of your host. The domain name may be needed if you want to purchase a new licence key.

If you want to deactivate an existing key, for example to switch keys, click *Deactivate*.

Then enter your new licence key in the input box and click *Activate*.

<img src="./media/images_image5.png" width="562" height="434">

After the licence has been activated, the licence information is shown. If there is an error, for example if the *Demo* button does not disappear, please contact `support@caxperts.com`.

If the licence was activated successfully, click *Close* to close the window and start using your application.

> The licence setup needs to be completed only once per host. All CAXperts applications use the same licence key, and activating it in one application activates it for all CAXperts applications on that machine.

---

# SpecXpert — Introduction

# Introduction

-   **SpecXpert** places all components in user-selected Intergraph Smart 3D piping specifications and reports errors through Smart 3D to-do list entries and additional reports.

-   **SpecXpert** recreates bulkload files in Microsoft Excel format for **Smart 3D** from the **Smart 3D** database.

---

# SpecXpert — Prerequisites

# Prerequisites

-   Supported operating systems:
    -   Microsoft Windows XP
    -   Microsoft Windows Server 2003
    -   Microsoft Windows Vista
    -   Microsoft Windows 7 or later
-   Intergraph Smart 3D 2011 or later
-   Microsoft Office 2003 or later

---

# SpecXpert — Usage

# Usage

When you run the software, the main window shows a drop-down list of available plants on your current Smart 3D site.

![](./media/image5.png)

After selecting a plant from this list, **select a permission group that is allowed to place piping components**.

![](./media/image6.png)

**SpecXpert** will list the piping specifications of this plant:

![](./media/image7.png)

## Test specifications

Select the checkboxes for the specifications, and nominal piping diameters, that you want to test.

If you want to exclude certain parts from the test, add them to an Excel exclusion list, sheet name `Excluded`, and select the Excel file in **Additional Requirements** (![](./media/image8.png)). Example files are included in the `templates` folder of SpecXpert. Wildcards are currently not supported, that is, all columns must be filled in.
                                                                
If the schedule thicknesses are calculated according to P/T conditions by using `ThicknessDataRule` during placement, for example `1C0031`, `NPD26-NPD36`, `FirstSizeSchedule=ASME B31.3`, it is possible to define user-defined values for P/T pairs. The pipeline name created by SpecXpert contains the P/T parameter. Add the P/T pairs in the `UserDefined-DesignPTPipeRun` sheet and select the Excel file in **Additional Requirements** (![](./media/image8.png)). Example files are included in the `templates` folder of SpecXpert.

In the **Output folder** field you can enter manually, or use the
\"\...\" button to select, an output path and filename for the resultant
report database.

Press **Run** to start placement of the piping specification parts. If you use **Run for median NPD**, only the median NPD size of the selected specifications is placed.

> Please use Intergraph's **Modify Site and Schema Location** tool to change the Smart 3D site.

For larger specifications, the placement process typically takes several hours. The placed components from the selected specifications will resemble the following:

![](./media/image9.png)

Each spec is placed in the \"SpecXpert\" piping system under a pipeline
named after the specification.

Parts are placed on pipe runs according to their nominal piping diameter. There is one main pipe run per NPD, together with a separate pipe run for each branch. The main pipe runs are vertically aligned from the smallest to the largest diameter.

### Report output

The final outputs of SpecXpert are a Microsoft Excel (`*.xlsx`) file and a Microsoft Access database (`*.mdb`) file. These files contain several objects:

-   "StatusPlacedSpecParts" sheet / table

-   "ToDoList" sheet / table, presenting the erroneous parts.

-   "ExtractedPipingData" sheet / table

### To-do list item interpretation

When **SpecXpert** has finished placing the parts from the selected specifications, the next task is to interpret the to-do list items. These can be found either in the SpecXpert report database or in the to-do list itself within Smart 3D. The report database presents similar errors only once, which makes it easier to see the actual number of specification issues.

While there are numerous rules in Smart specification writing that must
be followed, this section will serve as a guideline for interpreting
certain classes of errors that can abnormally occur when using
SpecXpert.

Please refer to the Intergraph Smart 3D troubleshooting guide for detailed listings of all possible to-do list items.

#### False positives

Although every effort has been made to limit the number of 'false positive' to-do list entries reported by **SpecXpert**, these false errors may still occur under certain conditions.

One example of this case is with plug or end cap components where the
end preparation of the connection port is not compatible with the pipe
stock. This can arise when e.g. a plug was included for use with an
instrument valve with a distinct end preparation.

Although **SpecXpert** can save countless hours of repetitive work, interpretation of the results still requires the understanding and domain knowledge of a trained specification writer.

#### Duplicate to-do list items

For many errors, a to-do list item will be created for each of the parts
involved. For instance, if a required flange size does not exist, a \"No
mating flange found\" item will be created for both the pipe stock
feature as well as the component feature. In these situations, fixing
one issue will typically resolve multiple errors.

#### No part found

To-do list items that begin with \"No Part Found in Catalog\" indicate
that the required part was not found in the catalogue.

This will often occur when SpecXpert attempts to place parts of a
certain NPD on a pipe run whose pipe stock NPD is not included in the
spec. For instance, **SpecXpert** places an olet part with a connection
port of NPD 1.5 in. The specification only consists of stock from 2
inches upwards. The branch from the olet will indicate that \"No Part
Found in Catalog Data\". This can be interpreted as either an error or a
false positive if the part is understood to require a spec-break.

#### No \<End Preparation\> x \<End Preparation\> mating part found

This error indicates that the end preparation of the part is not
compatible with the allowed end preparations of the pipe stock. The
above \"\<End Preparation\>\" will be replaced with the name of the
incompatible end preparation.

## Export specifications

<img src="./media/image10.png" width="680" height="341">

Select the specifications that you want to export in the tree view and choose which type of bulkload files to export from the **BulkLoadGenerator** tab. Then press **Extract bulkload sheets**.

To avoid empty sheets, select **Delete empty sheets**. To create only one workbook for more than one selected piping material class, select **One workbook for spec. data**:

This process typically takes several minutes. The resulting bulkload sheets are written to the folder specified in **Output folder**.

All options except the first option create Excel workbooks that are
related to the option name. The first option **Pipe spec with parts**
creates three types of workbooks:

-   **Specification data.** Excel workbook(s) for selected piping
    specification(s) with specification-dependent data sheets that
    include the attribute **SpecName** (e.g. "PipingCommodityFilter",
    "ServiceLimits", "NominalDiameters" etc.).\
    ![](./media/image11.png)

-   **Piping catalog data.** One Excel workbook for all selected piping
    specifications with their part data sheets (e.g. parts, valves,
    operators etc.).\
    ![](./media/image12.png)

-   **Material control data.** One Excel workbook for all selected
    piping specifications with their part-dependent data (e.g. for
    pipes, valves, operators etc.).\
    ![](./media/image13.png)

-   **Symbol icons.** Symbol icons for all part classes.
	It doesn\'t exist any information about the symbol icon name or
	symbol icon folder in the Smart 3D catalogue database.
											
	The symbol icon file listed in \...\\SharedContentXYZ\\SymbolIcon
	folder (.gif, .bmp or .jpg) is only used during the bulkload
	process.                      
											
	The bulkload utility takes the symbol icon file according to the
	name and folder in the part class sheet and saves the graphic
	in a BLOB (binary large object) attribute on part level in the
	Smart 3D catalogue, but it isn\'t saved any information about
	the original name and folder on part or part class level in the
	Smart 3D catalogue database.
											
	When SpecXpert has to export the symbol icon information it
	extracts the graphics out of the BLOB and saves it to a GIF file
	in a special directory named \"BulkloadGenerator\". The symbol
	icon name is created out of the part class name and the attribute
	PartDataBasis. To reuse the exported data copy the
	\"BulkloadGenerator\" directory (including all .gif files) to the
	..\\SharedContentXYZ\\SymbolIcon folder assigned to the Smart 3D
	catalogue where you want to reuse the data.

## Compare specifications

**SpecXpert** enables the user to compare specifications against each other. First select the type of bulkload sheets to compare, for example **Codelists**, **Generic data**, or **Plant rules**.

Ensure that the correct Smart 3D version is selected, then choose a folder with the requested files, a folder with the current files, and the result folder, and press **Compare**.

<img src="./media/image14.png" width="742" height="118">

> The exported file is formatted by using the unit settings defined in **Configure units**.

> If bulkload files with `PartClassType = PipeComponentClass`, `InstrumentClass`, or `SpecialtyClass` exist in the same folder, separate the files with different `PartClassType` values into different folders before comparison.

> Warning: all sheet names must be unique across all files in the selected folder and must appear in both folders to be compared. Otherwise, they are not compared at all.

## Units

Click **Configuration / Units** to open the **Configure units** form, which defines the output format of units of measure for new bulkload files.

![](./media/image15.png)

-   **Unit Type:** the quantity name of the unit.

-   **Unit:** the symbol of the unit (in Smart 3D notation).

-   **Decimal Precision:** the number of decimal places.

## Codelists

Click **Configuration / Codelists** to open **Configure Codelists**, where you define which codelists should be extracted.

![](./media/image16.png)

All codelists to be extracted are listed on the right-hand side. It is possible to move available or used codelists from one side to the other by using `>`, `<`, `>>`, or `<<`. The default codelists are defined by the codelists available in the bulkload files of the Smart 3D installation.

## Additional attribute PartNumber

The attribute PartNumber of PipeStockClass, PipeComponentClass or
ClampClass may be extracted by selecting the option **Extract attribute
PartNumber additionally** in the **Configuration** menu.

## Status bar

The status bar shows information about the current state of the software, for example progress bars. If you double-click it, the log file opens:

![](./media/image17.png)

---

# SpecXpert — Templates

# Templates

The resulting Access database can be configured by using a template.
The template file is usually located in:

`C:\Program Files (x86)\CAXperts\SpecXpert\Templates`

---

# SpecXpert — Command line mode

# Command line mode

**SpecXpert** also works in command-line mode.

`specxpert -?` shows the available command-line parameters.

<table>
  <tbody>
    <tr>
      <td><p>-plant:exampleplant</p></td>
      <td><ul>
      <li>Smart 3D plant name.</li>
      <li>Alias: -p</li>
      <li>Required.</li>
      </ul></td>
    </tr>
    <tr>
      <td><p>-permgroup:examplepermgr</p></td>
      <td><ul>
      <li>Permission group for <code>SpecTester</code>.</li>
      <li>Alias: -pg</li>
      <li>If not defined, last used.</li>
      <li>Optional.</li>
      </ul></td>
    </tr>
    <tr>
      <td><p>-mode:mode</p></td>
      <td><ul>
      <li>Execution modes:<br>ST  - <code>SpecTester</code><br>BLG - <code>BulkloadGenerator</code><br>CT  - <code>CompareTool</code></li>
      <li>Alias: -m</li>
      <li>If not defined, last used.</li>
      <li>Optional.</li>
      </ul></td>
    </tr>
    <tr>
      <td><p>-s3dversion:version</p></td>
      <td><ul>
      <li>Valid values for <code>CompareTool</code> templates:<br>2009.1<br>2011 SP1<br>2011 R1<br>2014<br>2014 R1<br>2016<br>2018<br>2019<br>V13<br>V13.1</li>
      <li>Alias: -v</li>
      <li>If not defined, last used.</li>
      <li>Optional.</li>
      </ul></td>
    </tr>
    <tr>
      <td><p>-output:outputpath</p></td>
      <td><ul>
      <li>Path of the result or report for <code>SpecTester</code>,
      <code>BulkloadGenerator</code>, or <code>CompareTool</code>.</li>
      <li>Alias: -out</li>
      <li>If not defined, last used.</li>
      <li>Optional.</li>
      </ul></td>
    </tr>
    <tr>
      <td><p>-current:currentpath</p></td>
      <td><ul>
      <li>Directory containing the current data for
      <code>CompareTool</code>.</li>
      <li>Alias: -curr</li>
      <li>If not defined, last used.</li>
      <li>Optional.</li>
      </ul></td>
    </tr>
    <tr>
      <td><p>-requested:requestedpath</p></td>
      <td><ul>
      <li>Directory containing the requested data for
      <code>CompareTool</code>.</li>
      <li>Alias: -requ</li>
      <li>If not defined, last used.</li>
      <li>Optional.</li>
      </ul></td>
    </tr>
    <tr>
      <td><p>-addreq:filename.xls</p></td>
      <td><ul>
      <li>Additional requirements for <code>SpecTester</code>.</li>
      <li>Valid sheet names in <code>filename.xls</code>:<br>Excluded<br>UserDefinedDesignPTPipeRun</li>
      <li>Alias: -ar (old: -excl and -exclusions)</li>
      <li>Optional.</li>
      </ul></td>
    </tr>
    <tr>
      <td><p>-config:filename.ini</p></td>
      <td><ul>
      <li>Configuration file containing all settings
      except parameters passed on the command line.</li>
      <li>Alias: -cfg</li>
      <li>If not defined, the configuration file of the
      plant is used.</li>
      <li>Optional.</li>
      </ul></td>
    </tr>
    <tr>
      <td><p>-continue</p></td>
      <td><ul>
      <li>Continue the last run if <code>SpecTester</code> was not
      completed.</li>
      <li>Alias: -cont</li>
      <li>Optional.</li>
      </ul></td>
    </tr>
    <tr>
      <td><p>-sheet:sheetcategory</p></td>
      <td><ul>
      <li>Sheet categories for <code>BulkloadGenerator</code> or
      <code>CompareTool</code>. This option can be used more
      than once on the command line.</li>
      <li>Valid values:<br>SP - Pipe spec with parts<br>CL - Codelists<br>GD - Generic data<br>PO - Plant options<br>PR - Plant rules<br>ID - Insulation data<br>PB - Pipe bending data<br>IS - Instruments and specialties</li>
      <li>Alias: -sh</li>
      <li>If not defined, last used.</li>
      <li>Optional.</li>
      </ul></td>
    </tr>
    <tr>
      <td><p>-spec:&quot;name|2 in|6 in&quot;</p></td>
      <td><ul>
      <li>Specification name for <code>SpecTester</code> and
      <code>BulkloadGenerator</code>. NPDs for <code>SpecTester</code>,
      if defined. This option can be used more than
      once on the command line.</li>
      <li>Alias: -s</li>
      <li>You can also specify a text file containing a
      list of specifications by using the <code>.lst</code>
      file extension.</li>
      <li>If not defined, last used.</li>
      <li>Optional.</li>
      </ul></td>
    </tr>
    <tr>
      <td><p>-oneworkbook:true</p></td>
      <td><ul>
      <li>Export specification data in one or more
      workbooks.</li>
      <li>Alias: -s1w</li>
      <li>Valid values: true or false</li>
      <li>If not defined, last used.</li>
      <li>Optional.</li>
      </ul></td>
    </tr>
    <tr>
      <td><p>-deleteemptysheets:true</p></td>
      <td><ul>
      <li>If no record exists, the sheet is deleted.</li>
      <li>Alias: -de</li>
      <li>Valid values: true or false</li>
      <li>If not defined, last used.</li>
      <li>Optional.</li>
      </ul></td>
    </tr>
  </tbody>
</table>

---

# SpecXpert — Examples

# Examples

## SpecTester Examples

1. Example 1:

   ```text
   "C:\Program Files\CAXperts\SpecXpert\SpecXpert.exe" -p:SP3DTrain -pg:Piping -m:ST -out:"c:\data\st 001" -s:1C0031 -s:2L1670 -s:1S3977
   ```

2. Example 2:

   ```text
   "C:\Program Files\CAXperts\SpecXpert\SpecXpert.exe" -p:SP3DTrain -pg:Piping -m:ST -out:"c:\data\st 002" -ar:"c:\data\st 002\exclude.xls" -s:"2L1670|3 in|4 in" -s:"1S3977|4 in|8 in"
   ```

3. Example 3:

   ```text
   "C:\Program Files\CAXperts\SpecXpert\SpecXpert.exe" -p:SP3DTrain -pg:"Permission Group1" -m:ST -out:"c:\data\st 003" -s:"1C0031 - Revision A" -s:"2L1670MM - Revision A"
   ```

## BulkLoadGenerator Examples

1. Example 1:

   ```text
   "C:\Program Files\CAXperts\SpecXpert\SpecXpert.exe" -p:SP3DTrain -m:BLG -out:"c:\data\blg 001" -sh:CL -sh:GD -sh:PO -sh:PR -sh:ID -sh:PB
   ```

2. Example 2:

   ```text
   "C:\Program Files\CAXperts\SpecXpert\SpecXpert.exe" -p:SP3DTrain -m:BLG -out:"c:\data\blg 002" -sh:SP -s:1C0031 -s:1S3977 -s:2L1670
   ```

3. Example 3:

   ```text
   "C:\Program Files\CAXperts\SpecXpert\SpecXpert.exe" -p:SP3DTrain -m:BLG -out:"c:\data\blg 003" -sh:SP -sh:CL -sh:GD -sh:PR -s:"1C0031 - Revision A" -s:"1C0031 - Revision B" -s:"2L1670MM - Revision A"
   ```

4. Example 4:

   ```text
   "C:\Program Files\CAXperts\SpecXpert\SpecXpert.exe" -p:SP3DTrain -m:BLG -out:"c:\data\blg 004" -cfg:"c:\data\blg 004\settings001.ini" -sh:SP -s:1S3977
   ```

5. Example 5:

   ```text
   "C:\Program Files\CAXperts\SpecXpert\SpecXpert.exe" -p:SP3DTrain -m:BLG -out:"c:\data\blg 005" -sh:SP -s:"c:\data\selectedspecs.lst"
   ```

## CompareTool Examples

1. Example 1:

   ```text
   "C:\Program Files\CAXperts\SpecXpert\SpecXpert.exe" -p:SP3DTrain -m:CT -v:"2011 R1" -out:"c:\data\ct 001\result" -curr:"c:\data\ct 001\curr" -requ:"c:\data\ct 001\requ" -sh:GD
   ```

2. Example 2:

   ```text
   "C:\Program Files\CAXperts\SpecXpert\SpecXpert.exe" -p:SP3DTrain -m:CT -v:2009.1 -out:"c:\data\ct 002\result" -curr:"c:\data\ct 002\curr" -requ:"c:\data\ct 002\requ" -sh:SP -sh:CL -sh:GD -sh:PO -sh:PR -sh:ID -sh:PB
   ```

---

# SpecXpert — Troubleshooting

# Troubleshooting

> In the current version of **SpecXpert**, only existing catalogue parts are tested. **SpecXpert** does **not** provide detailed information about what went wrong during placement, only that placement was not successful. To determine the exact reason why an error occurred, the complete specification is required for analysis.

> If custom naming rules are installed on the plant, **SpecXpert** may encounter problems during placement if the naming rules either slow down component placement or the resulting name cannot be created by the rule, for example because of missing system names. Ideally, the **SpecXpert** placement test should be run only after any problems with the specification that could be detected by the Intergraph **Verify Consistency Check** have already been resolved.

Here are some hints on how to interpret the **SpecXpert** test results:

## To-do list entries for automatically placed parts

Standard placement behaviour:

-   End components, such as end caps or blind flanges, are placed at the end of a branch.

-   All in-line parts are placed where the end preparations of the part are compatible with the pipe or mating flanges.

-   Parts with incompatible end preparations are placed at the end of a branch.

-   For all placed parts, to-do list entries are created by **Smart 3D** whenever something goes wrong during placement.

-   **SpecXpert** does not create the to-do list entries. It only summarises them in the provided reports.

-   The same to-do list entries would be created if the part were placed manually **under the same conditions**.

-   Under different conditions, it may be possible to place the part without an error.

-   Depending on the conditions, the part may be acceptable.

## Reasons for to-do list entries

There are many reasons why to-do list entries may be created in Smart 3D, and **SpecXpert** does not analyse the possible causes. It only lists the parts that caused problems. A specialist in writing pipe specifications must review these parts under the placement conditions for which they were intended. In many cases, the complete specification must be considered and reviewed in order to identify and resolve the problem.

Some common reasons for to-do list entries are:

-   Incompatible end preparations

-   Missing or broken symbol DLL

-   Missing generic data

-   Missing mating flange (selection basis = 5)

> Please note that analysis and correction of problems in pipe specifications are not included with **SpecXpert**, nor are they covered by the product maintenance agreement. CAXperts can offer an analysis service for problematic parts, but this service is charged separately. If you would like to use this service, please contact CAXperts to request a pricing proposal.

---

# UniversalReporter — UniversalReporter for SmartPlant

# UniversalReporter for SmartPlant

## Introduction

**UniversalReporterSuite** enables customers to extract, consolidate, review, report on, and edit data from different Intergraph® applications.

## Features

<img src="./media/images_image6.png" width="1074" height="598">

The **UniversalReporter Suite** includes the following features:

**ReportAdapter**s enable the software to generate data files in Microsoft® Access `.mdb` format for different Intergraph® applications. Each application requires its own adapter. Adapters are currently available for:

-   SmartPlant® 3D

-   SmartPlant® P&ID

-   Microsoft® Excel®

-   In-house applications

**UniversalReporter** enables intuitive reporting against data from a single application or from multiple applications.

-   exports data to Microsoft® Excel,

-   creates reports by using customised templates,

-   allows individual data views to be saved,

-   offers tools for configuring and renaming displayed attributes,

-   writes changes back to the application, an **EditAdapter** licence is required.

-   allows simultaneous use of data from all available **ReportAdapter**s,

-   enables simultaneous use of data from all available **ReportAdapter**s in order to compare different applications, projects, and versions,

-   creates multi-application windows incorporating data from many sources,

-   supports interaction between multi-application and single-application windows in order to enable quick comparison and editing.

> The software required to edit data is implemented in **UniversalReporter** and the **ReportAdapter**s, but it is available only if an **EditAdapter** licence is installed.

For editing data from in-house applications, an **EditAdapter** licence is not required.

## Prerequisites

Supported operating systems:

-   64-bit: Microsoft Windows 7 or later, Microsoft Windows Server 2008 or later

-   Intergraph SmartPlant® 3D 2014 or later

-   Intergraph SmartPlant® P&ID 4.3 or later

-   Microsoft Office

## Adapter Licences

Depending on the requirement, licences are issued for the following:

-   ReportAdapter for SmartPlant® 3D

-   ReportAdapter for SmartPlant® P&ID

-   ReportAdapter for Microsoft® Excel® (this adapter does not require a licence)

-   ApplicationReporter for SmartPlant®

-   UniversalReporter for SmartPlant®

-   EditAdapter for SmartPlant® 3D

-   EditAdapter for SmartPlant® P&ID

-   EditAdapter for Microsoft® Excel® (this adapter does not require a licence)

## Usage

### UniversalReporter

**UniversalReporter** creates reports from single applications as well as across multiple applications, and allows you to check, validate, and combine data *between* applications. It permits simultaneous use of data from available **ReportAdapter**s. The system also allows the creation of multi-application windows so that the user can compare data between different versions of the same project, or data between two different applications.

### Starting UniversalReporter

When **UniversalReporter** is opened, the system is typically set to recall the last used data files. However, if no history is available, or if the user chooses to open a different data file, the steps are as follows:

Navigate to **File → Open project** or use the hotkey **Alt+O**.

The **Open** dialogue box appears:

<img src="./media/images_image10.png" width="550" height="410">

Navigate to the directory where the data (`.mdb`) file is located. Select the file and click **Open**. The data file is then loaded.

> It is possible to open a database by dragging the `.mdb` file into the **UniversalReporter** window.

If you want to load a previous data file, select **File → Recent projects**.

<img src="./media/images_image12.png" width="368" height="126">

> It is possible to disable the automatic opening of the last project. This can be configured in the **Tools → Settings** menu.

> Data files can be created by one of the **ReportAdapter**s, or they can simply be Access database files, for example files created by an in-house application.

> If you open a project for the first time, no attributes are configured to be displayed unless a default has been set. Therefore, the **Attribute manager** is displayed so that the configuration can be defined, see *Customising the attributes panel*.

<img src="./media/images_image13.png" width="735" height="459">

### Graphical User Interface of UniversalReporter

The **UniversalReporter** database windows or tabs are split into three areas:

-   Attributes tree

-   Attributes panel

-   Data grid

> The areas can be resized and their visibility can be toggled. To show or hide the areas, use the appropriate buttons (<img src="./media/images_image14.png" width="47" height="16">). Resizing can be done by moving the bars between the areas (<img src="./media/images_image15.png" width="12" height="16">) and or by resizing the window itself.

### Attributes tree

![](./media/images_image16.png)

The **attributes tree** is a visual representation of the application database, exported with the **ReportAdapter** to a data (`.mdb`) file, refer to the **ReportAdapter** documentation for full details. Each node listed in the **attributes tree** can represent a table, column, or value in the data file, otherwise referred to respectively as an object, attribute, and value.

All nodes can be added or removed by using the context menu. Object nodes cannot be renamed. As shown in the figure above, the base nodes are always object nodes. These nodes represent each table in the data file.

The second visible node type is the attribute node. These nodes represent columns of selected tables in the data file. They can be distinguished from value nodes by bold text, and from object nodes by their position. Unlike value nodes and object nodes, attribute nodes can be renamed, see Appendix B: context menus.

The last node type shown in the figure is the value node. These represent each possible value of an attribute and are used as a filter and grouping mechanism for a view.

### Attributes panel

![](./media/images_image17.png)

The **attributes panel** lists attributes of a selected object in the **attributes tree** (see *Attributes tree*). Checking or un-checking an attribute adds/removes a column to/from the **data grid** (see *Data grid*). With the buttons on top of the **attribute panel** all items can be checked

(<img src="./media/images_image18.png" width="16" height="16">) or unchecked (<img src="./media/images_image19.png" width="16" height="16">).

If the status of the attributes panel shows **Max reached**, there are too many (\> 75 for windows with Edit functionality, \> 120 for windows without Edit functionality) attributes selected:

![](./media/images_image20.png)

The list of available attributes can be configured using the **attribute manager** window. (see *Customising the attributes panel*)

![](./media/images_image21.png)

**Path attributes** are shown in red italics. They refer to attributes that are in the current path of the selected node in the **attributes tree**, and are used to group and/or filter data in the **data grid**. **Path attributes** cannot be un-checked. To uncheck the attribute it has to be deselected in the **attributes tree**.

When selecting attributes, you can also choose to apply operators.

<img src="./media/images_image22.png" width="443" height="287">

There are five operators:

-   **MAX:** calculates the maximum of all values grouped in each row in the **data grid**. This operation cannot be selected with its corresponding attribute.

-   **MIN:** calculates the minimum of all values grouped in each row in the **data grid**. This operation cannot be selected with its corresponding attribute.

-   **SUM:** calculates the sum of all values grouped in each row in the **data grid.** This is the only value that can be selected with its parent attribute, as it won't create misleading results.

-   **COUNT:** displays the number of unique values of the attribute for each grouped row in the **data grid**. This operation cannot be selected with its corresponding attribute.

-   **VAL:** displays the numerical part of a string field

The operators that are allowed for each attribute are determined by the type of attribute.

Numeric attributes have all operators unless the attribute is contained in the path, then it only has a **SUM**.

Date attributes have all operators except **SUM**. If it is contained in the path, then no operator is possible.

Non-numeric attributes only enable **COUNT** and **VAL** operations.

Attributes with checked operator are shown in bold print. The data related to the checked operator is visible in the **data grid.**

> The order of columns shown on the attribute panel is alphabetical and does not correspond to the order of columns shown in the grid. Adding/enabling one additional column will append the column at the end of the grid.

### Data grid

<img src="./media/images_image23.png" width="744" height="88">

The **data grid** displays the data that is selected by the two other components, the **attributes tree** and **attributes panel**. All columns shown are based on selections made in the **attributes panel.**

The **data grid** has three types of columns:

-   The path column is shown in italics and reflects the **path attribute** selected. If there is a value selected in the **attributes tree,** the column cannot be filtered.

-   The operator column reflects an attribute with operator applied in the **attribute panel** (labelled with \_(of)\_).

-   The attribute columns reflect all other attributes selected in the **attribute panel**.

### Views

Views are saved program states and are visible as tabs above the **attributes tree, attributes panel** and **data grid**.

![](./media/images_image24.png)

Opening a view can be done in two ways, first, in the **File** menu, and secondly with the toolbar button
(![OpenView](./media/images_image25.png)).
Multiple views can be opened at the same time. Each view is displayed as a tab as seen above.

Creating a new view can be done just as easily, this also has a menu item in the **File** menu, and a toolbar
button(![NewView](./media/images_image26.png)).

Closing a view is done with the same methods as listed above and additionally with a context menu which is opened by right-clicking a tab. The toolbar button icon
is![DeleteView](./media/images_image27.png).

Views also have the **Save**
(![SaveView](./media/images_image28.png)), **Save as**, and the **Save all views** function, which can be found in one or more of the above mentioned menus or toolbars. Saving writes changes to a view into a **UniversalReporter view file** (.cvf). After saving, this file can be opened with any project of the same type of application.

When the view is locked, i.e. already open in another session (e.g. by another user), it will be opened read-only. The lock is indicated by a red dot on the view tab. From a technical point-of-view the lock is a lock file with extension .ovf. The lock can be removed using **Tools** → **Remove readonly from view**.

> When opening a view make sure, that the project used to open it has the required objects and attributes. Otherwise the software will prompt to add the missing attributes.

> Before changes are applied, a backup file (.bak) of the current view will be created

> Multi-window views may request additional information on saving.

> Multi-window views may require mapping when opening. See: *Mapping for further information*

### Customising the attributes panel

> If there is more than one window of the same application opened, this menu entry won't be available.

Click on **Tools** → **Attribute manager**. The following dialogue box appears for the application of the active window.

![](./media/images_image29.png)

Expanding an object node will list all available attribute nodes, attribute values will be shown in the tooltip.

Select the required attributes by checking the preceding checkbox. Press the **Add** button (<img src="./media/images_image30.png" width="16" height="16">)
to add the selected nodes to the **Visible** panel. Click on the (<img src="./media/images_image31.png" width="16" height="16">) button to remove selected attributes from the left tree, after selecting them.

Press the **OK** button to apply the changes. The **Don't show attributes without values** check box enables or disables attributes under all object nodes which have no values in the database (looking for empty values will take some time).

The attributes newly added will at once be visible in **attributes panel** but won't be visible straight away on the **attributes tree**.
If required, use context menu or equivalent toolbar button to add attributes to the **attributes tree**.

> When a project is opened for the first time, no attributes are configured to be displayed. This behaviour
can be changed by using the attribute manager to define a default configuration. Select all tables and attributes to be displayed and use the set as default button to define or to change the default attributes.

![](./media/images_image32.png)

To remove attributes for the purpose of clearness, select **Remove attribute** from the context menu of the desired attribute node.
Removing an attribute can also be done by dragging it to its object node.

![](./media/images_image33.png)

### Customising attributes names (glossary function)

The system permits the user to setup a glossary for attribute nodes. Each application can be setup to use its own glossary by changing the glossary path in the settings window (see *Settings*). There can be multiple sets of glossary entries.

> System will generate first set of glossary call "Default" but user will need to provide the new attribute names. You can also use the context menu item Rename of the treeview to modify the glossary.

From main menu, select **Tools → Glossary → Edit glossary**. This will open the glossary table in Microsoft**®** Access.

![](./media/images_image34.png)

Double click on the glossary table. There are two root columns in this table. The **Table_Name**, and the **Column_Name**. Each column being self-explanatory. Each additional column defines a glossary entry. The **Default** column is the first entry listed. Every additional column will add another glossary entry.

<img src="./media/images_image35.png" width="718" height="437">

The table above shows an example of a glossary entry with the name \"German\".

Then close **Microsoft® Access**.

In **UniversalReporter**, if system glossary ('NONE') is selected you will see the original attribute names.

![](./media/images_image36.png)

To switch glossary select **Tools → Glossary → #The Glossary you created#.** See example below:

![](./media/images_image37.png)

Selecting German, you will see the attribute names as translated in this glossary.

Some ReportAdapters (e.g. **ReportAdapter for SmartPlant P&ID, SmartPlant Instrumentation** and **SmartPlant Electrical**) provide the attribute names together with the display names. In these cases the glossary includes an additional column with the display names (e.g. PidDisplayName, InstDisplayName, ElDisplayName).

<img src="./media/images_image38.png" width="706" height="286">

This additional column is created/updated automatically whenever a database, which contains display names, is loaded.

> During update only new display names will be added, existing one will not be overwritten. Thus, the user can modify these names in the glossary and the modifications are kept.

### Filtering and sorting views

Arranging the views is essential to the **UniversalReporter**. In order to keep the process simple, all actions can be done with a mouse. The following chapters will help you to set up each view part by part. All changes listed here are saved to a view file (.cvf) when saving the view.

#### Filter and grouping functionality

The **attributes tree** can be used to filter attribute and value. For this purpose the desired attribute nodes may be nested. For example, if you wish to know for a particular buyer, what were the PO_NUMBERs created by him/her, follow the steps below:

![](./media/images_image39.png)

-   Add the object \[PO_HEADERS\] to the **attributes tree**.

-   Add the attribute \[BUYER\] and \[PO_NUMBER\] to the object in the **attributes tree.**

-   Grab and drag the **PO_NUMBER** node and drop this on the **BUYER** node.

-   Expand the **BUYER** node then select a buyer. Expand this value and you will see the **PO_NUMBER** node nested within this buyer.

-   Expand the **PO_NUMBER** node under the select buyer and you will see all PO_NUMBERs created by this buyer.

![](./media/images_image40.png)

This process can be repeated recursively until the desired granularity has been reached.

#### Search in attributes tree

To search the attributes tree, select **Search in attributes tree** on the **Edit** menu.

This standard search window will show up:

![](./media/images_image41.png)

Option **One result per attribute** is only available if **Attributes** is selected for field **Look in**. Checking/unchecking this option leads to different results only if the grouping functionality of the **attributes tree** is used (see *Filter and grouping functionality*).
If the option is checked the search will show the matching name of nested attributes only once.

#### Data grid customising and filter functions

Within the **data grid**, columns can be sorted, moved, filtered or resized.

##### Sorting

Click the column header to sort the row ascending and once more in descending order.

<img src="./media/images_image42.png" width="726" height="327">

##### Custom sorting

Custom sorting permits multilevel sorting to be applied.

In the **attributes tree**, ensure you have selected the object node required and the relevant columns are visible on the **data grid**.

Click on the **Custom
sort**![](./media/images_image43.png) icon. The following dialogue appears:

<img src="./media/images_image44.png" width="669" height="569">

Add the first level of sorting required.

<img src="./media/images_image45.png" width="669" height="569">

Add the second level, third level, etc. To reorder the sorting, use the arrow icons. To remove a level of sorting, use the
**Delete**![](./media/images_image46.png) icon. Use
**Clear**![](./media/images_image47.png) icon to remove custom sorting. Click OK when complete. System will apply custom sort to the **data grid**.

##### Reordering/moving

Grab and drag the column you want to move and drop it to its new location (marked by a red arrow).

<img src="./media/images_image48.png" width="729" height="327">

##### Filtering

<img src="./media/images_image49.png" width="729" height="327">

The **NULL** value will filter for all empty values in the column, while the **NOT-NULL** does the opposite.

It is also possible to filter manually by typing into the filter field.
Possible operators while manually filtering include **\<**, **\>**, **\<=**, **\>=**, **\<\>**, **\*** or "%". It is also possible to use the **&&** (AND) and the \"\|\|\" (OR) conjunction to place multiple filters for the same column.

>  "\*" and "%"can be used interchangeably, they can be used before text, after text or both but not in text: valid uses: "S\*", "\*S" or "\*S\*"; non-valid uses: "S\*S

> Filters are not case sensitive, with the exception of NULL and NOT-NULL. Thus it is possible to filter for the value null.

##### Filter by list

<img src="./media/images_image50.png" width="318" height="282">

The above window displays the **Filter by list** option, which allows selecting multiple values to filter with using the \[ctrl\] button on clicking, or by dragging the mouse over multiple values. This option can be found in the context menu of the column which is to be filtered.

##### Filter by selection

Use the context menu item **Filter by selection** to filter all items so that only items with the selected value will be shown.

##### Exclude selection

Use the context menu item **Exclude selection** to filter all items so that only items without the selected value will be shown.

##### Append exclude selection

Use the context menu item **Append exclude selection** to remove all items with the selected value from the current filter.

##### Filter multiple columns

If filters are set to more than one column then these filters by default are applied as "AND" filters which means that all criteria have to be fulfilled.

To change this behaviour, use the toggle button **AND/OR** to switch between this "AND" mode and an "OR" mode where the column filters are applied in a way that all rows where the data match at least one of the
filters are displayed.

##### Resizing

It is possible to resize each column in the data grid as seen below. There are two possibilities to do this.

<img src="./media/images_image51.png" width="729" height="327">

The first possibility is to drag the space between columns to the right in order to increase the size, and to the left in order to decrease the size. Dragging must be done at the header level of the grid. See above diagram.

There is also the possibility to auto size each column to the largest value. This is done by simply double clicking the space between the columns.

##### Removing Columns

It is possible to remove a column with ***Right Mouse / Remove*** column on the data in the selected column. It is also possible to remove a range of columns by selecting a range of columns and then selecting ***Right Mouse / Remove Columns***

##### Freezing Columns

It is possible to freeze a column with ***Right Mouse / Freeze column*** on the data in the selected column.

![](./media/images_image52.png)

## Reporting

### Exporting to Microsoft Excel

![](./media/images_image53.png)

-   **Export selected rows only** is only available, if rows are selected in the **data grid**. To select a row, click on its header.
    Use Shift and Ctrl keys to select more than one row. The report will then be created for the selected rows only.

-   **Export grid with colour** creates an Excel report directly from the **data grid**, where cells are coloured like in grid. If this option is selected, no template can be used.

-   **Create multi report based on key attribute:** is only available, if a value was selected in the **attribute tree**. The system has the ability for creating multiple sheets of different reports in one file, for a selected value of a key attribute. The report template then has to contain multiple template sheets which all share this key attribute (see *Creating multiple sheets report associated to a value*).

-   **Create one file per\...** (e.g. Create one file per \'Spec Name\') is only available, if an attribute (e.g. spec name) or a value within this attribute was selected in the **attribute tree**. Using this option, for every value of the selected attribute a new report file will be created (e.g. a separate file will be created for every Spec Name) see also *Creating a file per attribute value*.

-   **Add code lists (PID and 3D only):** With this option selected an additional sheet (CodeLists) will be added to the Excel report, which contains all code list values for the extracted attributes. In the report sheets, the code listed attributes refer to this additional sheet such that the code list values can be selected from a drop down field for changing the data.

#### Template-free export to Excel

The most direct method is to export data to Excel without using a template. The steps are:

-   Ensure you are on the data grid which you want to export. Click on **Export to Excel**
    (![Export2Excel](./media/images_image54.png)) or use menu **Tools** -\> **Export to Excel**.

-   Ensure the checkbox **Use a template** is not ticked then click on the **Create report** button.

![](./media/images_image55.png)

-   Decide name and location where you wish to save the export file. Click **Save**.

The system will then generate a report and display the following dialogue box.

![](./media/images_image56.png)

Click **Yes** to display the Excel report.

<img src="./media/images_image57.png" width="706" height="409">

#### Export to Excel using a template

The steps to create an Excel report using a template are:

Ensure you are on the data grid which you want to export. Click on the
**Export to Excel**
(![Export2Excel](./media/images_image54.png)) toolbar button. The following dialogue window will appear.

![](./media/images_image58.png)

You have several options to obtain the template file you need:

Use the **Template file path** combo box showing the templates used before; select your template if it is available from the drop down list.

If the file is not available on the drop down list and you want to use an existing template, click on the **File** button. A dialogue box will appear. Locate the template and click **Open**.

> Make sure that the selected template fits the view to be reported. If there are columns in the template with attributes not available in the view then these columns will not be filled with values.

If you want to create a new template, click on the **New** button. A dialogue box will appear. Enter the filename required and click **Save**.

To open a template for editing: For the first two options click the **Edit** button. For the third option, the system will automatically open the template for editing when you click **Save**.

The standard template looks somewhat like the sample shown below.

> There is an add-in toolbar available in Excel to help you create and customise the current
template.

![](./media/images_image59.png)

The following options/settings are available on the add-in:

-   **Create standard report:** apply the header and skip lines setting, and rebuild the sheet.

-   **Add column:** insert a column marker for the selected column in the cell currently selected. The displayed columns are those available in **UniversalReporter**.

-   **Add variable:** insert a report variable from list of values. (see *below*)

-   **Add sum:** inserts a summed value to the report. Currently only values which are recognized as numbers in the database are listed.
    Depending on wherer a sum field is placed sum types can change. See *Creating reports with sub header, sub footer and sub total)*.

-   **Header lines:** insert the number of rows that should be above the column markers to add header information.

-   **Sub header lines:** insert the number of rows where sub header text and attributes should be defined. (see *Creating reports with sub header, sub footer and sub total, see also* the Lab
    *UniversalReporterSuite 3D* on the **Help** menu).

-   **Sub footer lines:** (see *Creating reports with sub header, sub footer and sub total*).

-   **Skip lines between rows:** inserts the given number of empty rows between each line(s) with data.

Additionally it is possible to insert pictures (i.e. your company logo), text and/or text formatting into the header rows. See sample below.

![](./media/images_image60.png)

#### Export to Excel using report variables

You can add a report variable to the template. First you must create this variable in **UniversalReporter**. To do this:

-   From main menu, select **Tools → Report variable definition**. This will open the **Report variable definition** dialogue box.

![](./media/images_image61.png)

The top part of the variable definition form displays system and plan specific variables which are read from the system or the database of the current active data window. These are not editable.

The user defined section displays the variables that are defined per user, and can be edited. By entering the name and value of the variable in the **Name** and **Value** fields respectively.

Now you can add the field to your template. See below:

![](./media/images_image62.png)

> It is also possible to lock the cells in a template and then protect the sheet, in order to disable the
editing of certain extracted values. Protected sheets are not allowed to have passwords. This does not include protecting a workbook.

#### **Variable "Asbuilt-name**

For PID data (assuming the PID ReportAdapter version is at least 3.2.0.39) in the case or projects located underneath an As-Built plant the variable **Asbuilt-name** is available for reporting.

![](./media/images_image63.png)

#### Creating multiline report

By simply spreading the column marker across more than one line a multi-lined report is created.

![](./media/images_image64.png)

#### Creating multipage report

It is also possible to create multipage reports. This is achieved by placing one or more column markers within the header area. Placing for example #Originator# into the header area will create one sheet for each different originator.

![](./media/images_image65.png)

> The multipage report template is not to be used for creating a "Multiple sheets report associated to a particular value".

Close and save the template when you have finished.

The **Create report** dialogue will be displayed again.

![](./media/images_image66.png)

Ensure the **Use a template** check box has been ticked, click **Create report** button to generate the report.

> If the **Use a template** check box is not ticked, the system will generate the report in the standard report mode.

Below is the finished report utilising multipage. In this case each
sheet represents the originator name.

![](./media/images_image67.png)

#### Creating reports with header and footer

There are two possible header and footer types. The first are report headers and footers. Report headers and footers are added directly to the report, using the template header rows configuration in the add-ins toolbar. This header type is visible on each sheet in the report, but not on each page. The same pertains to the footer. The amount of rows added to the footer does not need to be defined, since it is simply calculated by designating everything below the last data row. Entering a "#" value into the footer may result in strange effects, though it is possible to add a sum field into both the header and footer to receive the total value of that column.

The second possible header and footer are those that are defined by the Excel pages. It is possible to use the Excel predefined values in these  fields (i.e. page number, date, etc.) as well as **UniversalReporter** variables, both user and predefined ("%" fields).

> Excel page headers are only usable for Excel versions after 2003/XP, for older versions, this feature is ignored.

<img src="./media/images_image68.png" width="780" height="442">

The above picture displays the usage of a standard template with the excel page header and footer using the variables %plantname% and %date% in the header, and %user% in the footer.

#### Creating reports with sub header, sub footer and sub totals

In addition to Report headers and footers, it is possible to add sub headers and sub footers. Both are defined by adding the number of rows into the add-ins toolbar. The sub header rows are counted directly after the header rows end, and the sub footer rows start directly after the last data row (rows with "#" fields).

The sub header row is used to create a master-detail-report by including a data field ("#") in the sub header rows; the report will sort the following data rows to match the values in the sub header as seen in the following example.

> In addition to data fields the sub header can contain variable fields. Any sum fields entered into these rows will result in the report total and not the sum of the area between each sub header and footer.

The sub footer rows cannot contain any data fields but can contain variable and sum fields. Sum fields in the sub footer rows calculate different from sum fields elsewhere, the total of the area between the last sub header and sub footer instead of the whole total of the data field.

Even though sum fields are predefined in the add-ins toolbar, these values only display fields that are recognized by the database as a number, in which case some attributes may be numbers as well but not listed in the dropdown in the toolbar.

It is still possible to sum these fields even if they are not in the dropdown, by manually adding the field name (original and not display name) between two "\|" symbols. Additionally numbers which contain units of measurement and other strings can also be totalled.

![](./media/images_image69.png)

The above picture shows a report in which piping system is used as a sub header, and the estimated length of each pipe run of that piping system is totalled.

### Creating multiple sheets report associated with a value

The system has the ability for creating multiple sheets of different reports for a single value selected. First you have to set-up a template, which has the value as the key parameter, e.g.to create a report which lists PO_HEADERS on the first sheet and with PO_LINE_ITEMS as the second sheet. The steps are:

Create a template report for table PO_HEADERS as per normal (*see Exporting to Excel)*. Give the template a unique filename say PO_header_01.xlsx.

Similarly create another template report for table PO_LINE_ITEMS as per normal. Give the template a unique filename say PO_line_items_01.xlsx.

> It is vital that both templates share one unique attribute. In the example below, this will be POH_ID.

Now combine these two worksheets into one Excel template. To combine, copy PO_header_01.xlsx to another Excel File, say PO_info.xlsx.

![](./media/images_image70.png)

Open PO_info.xlsx. Rename the sheet name from "Report" to the object name which this template was generated with, in this case it will be "PO_HEADERS".

![](./media/images_image71.png)

![](./media/images_image72.png)

![](./media/images_image73.png)

Copy the second worksheet from PO_line_items_01.xlsx into PO_info.xlsx.

![](./media/images_image74.png)

Rename the sheet name from "Report" to the object name it was originally created for, in this case it will be "PO_LINE_ITEMS".

![](./media/images_image75.png)

![](./media/images_image76.png)

**Save** and **Close** the updated template report.

Return to **UniversalReporter**.

In the **Tree filter**, for the object PO_HEADERS, select a value for the attribute POH_ID. Say we select POH_ID = "5001". Click on the **Export to Excel**
(![Export2Excel](./media/images_image54.png)) button. The **Create report** dialogue will appear.

Locate the template required. Tick the **One template per table attribute** checkbox and the **Use a template** checkbox .Click **Create report.** The system will ask you to save the output file and after the report was created if you like to review the output file. All data related to POH_ID = "5001" will be reported to the multiple sheets.

![](./media/images_image77.png)

![](./media/images_image78.png)

### Creating a file per attribute value

The system can create a file for each value of a selected attribute, e.g.:

The objective is to create a report for each buyer listing their purchase orders.

First, in the **tree filter** grid, under **PO_HEADERS** object, you will need to add the attribute **BUYER**.

![](./media/images_image79.png)

In the **attributes panel**, select the fields you need in your report.
Check the data in the **data grid filter**, with the **attribute BUYER** selected in the tree, click on the **Export to Excel**![](./media/images_image80.png) icon.

Ensure the check boxes "Create one file per 'BUYER'" and "Use a template" are ticked and a template is selected.

Click **Create report** button to generate multiple files. Select a folder to which the multiple files shall be saved. The program will open this folder after the reports were created. The **BUYER** values are added to the respective report file names.

The resulting Excel files should look like this:

![](./media/images_image81.png)

Sample report for **BUYER** "PA"

![](./media/images_image82.png)

Sample report for **BUYER** "PABROAL"

### Creating template with Excel function

You can also include Excel functions within a template which you create. For example, you can **Concatenate** fields, use the **If** function,
mathematical functions etc. in the template.

> Excel functions in report templates are only working if the first column in the dataset is not a formula but an attribute placeholder. If the first column in the dataset is a formula, you need to add a column before it with a dummy field #DUMMY#.

## Report generation using URB files

**UniversalReporter** can be used from the command line to automate the Excel report creation.

> Command line mode does not support reports from multi-application or rule data windows.

Prior to usage, the report which needs to be run in batch mode needs to be configured and saved as a **UniversalReporter** batch file (.urb).
This can be done in the **Create report window**.

![](./media/images_image83.png)

Clicking the **Create batch file** button will open a new window in which two fields need to be filled.

![](./media/images_image84.png)

The **Excel report file** defines the destination for the resulting report Excel file. The file name can automatically be modified with the date at the end (i.e. c:\\ReportFile.xlsx becomes c:\\ReportFile_2012_01_01.xlsx) when the **Add date to Excel file** option is checked.

The **Batch output file** path defines the destination and file name of the resulting UniversalReporter batch file (.urb) which contains all the information which is needed in order to create the report in command line mode.

Using the resulting urb file it is possible to create a report in the command line mode as follows:

C:\\\> "C:\\Program Files\\CAXperts\\UniversalReporter\\UniversalReporter.exe"
\[Path to urb file\]

The batch file resulting report is then created at the location defined
in the urb file.

Options (like **Use template**) selected in the **Create report window** while creating the urb file are stored there and will be applied in batch mode.

> Some export options, e.g. **Export selected rows only**, **Export with grid colour**, cannot be checked for batch mode. Glossary is not supported.

> The batch mode can be started with command line or by double-clicking the .urb file.

## Reporting in command line mode with parameters

**UniversalReporter** can be used from the command line to automate the Excel report creation.

> Command line mode does not support reports from multi-application or rule data windows.

These parameters are available via command line:

![](./media/images_image85.png)

## Multi-application windows
(not available in ApplicationReporter)

### Opening multiple projects simultaneously

You can open multiple projects from different applications simultaneously if you have the **UniversalReporter** licence.

Open a project from one application. Click on **File → Open project** or **Recent projects** and choose the database you want to open.

Similarly, open the second project from the same or another application by repeating this step. This time choose a different database. Repeat this for additional databases.

Use the windows icons (![](./media/images_image86.png)) to organise your projects on the screen.

For example, tile the windows horizontally by using the ![](./media/images_image87.png)**Tile windows horizontally** icon.

![](./media/images_image88.png)

> When you double click on the window titles, they will get arranged as tabs and vice-versa.

### Links for multi-application windows

To activate the multi-application windows, a link for the two objects to be joined must be available in the **Links configurator**.

#### Links

In general, links work by associating two attributes together. These attributes can come from any of the supported applications and can be grouped for more complex joins. Links also allow for concatenated attributes or SQL functions, meaning that even custom naming rules can be associated across applications.

Valid application acronyms are:

-   PID for SmartPlant® P&ID applications

-   Ins for SmartPlant® Instrumentation applications

-   El for SmartPlant® Electrical applications

-   PDS for PDS® applications

-   3D for SmartPlant® 3D applications

-   Mat for SmartPlant® Materials applications

-   Xls for Microsoft® Excel applications

-   PDMS for PDMS applications

-   Gen mdb files not resulting from a Report Adapter

The links can be defined between the same applications (e.g. to compare different versions of a project) or between different applications. The following is an example for a simple link between equipment from P&ID
and 3D:

<table>
<tr><th>Link_Name</th><th>Application1</th><th>Table1</th><th>Attribute1</th><th>Application2</th><th>Table2</th><th>Attribute2</th></tr>
<tr><td>

Equipment

</td><td>

PID

</td><td>

Eqp_Nozzle

</td><td>

Equipment_ITEMTAG

</td><td>

3D

</td><td>

Equipment

</td><td>

Equipment Name

</td></tr>
</table>

In the above example the P&ID Eqp_Nozzle table will be linked to the 3D equipment table only where the ItemTag in P&ID matches the EquipmentName in 3D.

Each attribute used for linking is identified by its application, table name, and attribute name.

> The link attributes (or link expressions) have to be unique to avoid cross linking.

The \'Link_Name\' column is an identifier that is used both as a display name when creating multi-windows inside the **UniversalReporter** software as well as a \'group name\' when creating more complex joins.
It should be descriptive and must be unique to the set of attributes.

The following example (based on the previous example) shows a more complex link using multiple entries:

<table>
<tr><th>Link_Name</th><th>Application1</th><th>Table1</th><th>Attribute1</th><th>Application2</th><th>Table2</th><th>Attribute2</th></tr>
<tr><td>

NozzleList

</td><td>

PID

</td><td>

Eqp_Nozzle

</td><td>

Equipment_ITEMTAG

</td><td>

3D

</td><td>

Equipment

</td><td>

Equipment Name

</td></tr>
<tr><td>

NozzleList

</td><td>

PID

</td><td>

Eqp_Nozzle

</td><td>

Nozzle_ITEMTAG

</td><td>

3D

</td><td>

Equipment

</td><td>

Nozzle Name

</td></tr>
</table>

In the above example the two applications will be linked both on matching equipment names and matching nozzle names. In this way more complicated links can be built up to meet additional requirements.

The final example shows the use of concatenated attributes:

<table>
<tr><td>

Link_Name

</td><td>

Application1

</td><td>

Table1

</td><td>

Attribute1 

</td><td>

Application2

</td><td>

Table2

</td><td>

Auttribute2

</td></tr>
<tr><td>

PippeLines

</td><td>

PID

</td><td>

InlineComp_prun

</td><td>

\[Piperun_operfluidcode\]  & \"-\" &  \[Piperun_tagsequenceno\]

</td><td>

3D

</td><td>

Piping

</td><td>

Pipeline System

</td></tr>
</table>

In the above example the pipeline tables are being linked from P&ID to 3D. In the example 3D is also using a custom naming rule that needs to be matched in P&ID.

The syntax used for concatenating multiple attributes follows the
standard Microsoft Access rules:

-   All attribute names must be enclosed in \'\[\]\' square brackets.

-   All separator characters must be enclosed in \'\"\"\' quotation
    marks.

-   The \'&\' symbol is used as the concatenation operator.

#### Links configurator

The links configurator is a tool to display, create, delete and edit the links. It can be opened by clicking the **Tools -\> Links configurator**
menu entry.

To display an existing link select a link name from the links configurator toolbar.

![C:\\Users\\th\\Desktop\\Universal\\1.png](./media/images_image89.png)

The meaning of the fields is as follows:

1.  The links configurator toolbar

2.  Table containing all attributes used for the link definition

3.  Attribute (or multiple attributes) used for the link definition

4.  Connection between two tables

5.  Drop down list for additional SQL functions to be executed with
    > attribute values

6.  SQL function (e.g. Left) showing its parameters in brackets

7.  Field for text to be concatenated with the attribute (as prefix or
    > suffix) or for text between two concatenated attributes.

8.  This grid shows all links for the selected link name.

9.  These columns show the associated applications

10. These columns show the associated table names

11. These columns contain either a single attribute name or a more
    > complex SQL statement that defines the link. The whole cell will
    > be coloured if there is a problem to interpret the expression.
    > This occurs either on syntax errors or missing capability to
    > display it correctly (see *Create new links*).

#### SQL Functions

For link definitions with complex SQL statements you can choose between
a set of predefined functions:

**Trim**: Removes leading and trailing blanks

**Upper**: Changes all characters to upper case

**Lower**: Changes all characters to lower case

There also exists a set of functions that requires some parameters to be
set:

**Left**: Uses only the first *n* characters.

**Right**: Uses only the last *n* characters.

**Mid**: Uses all characters beginning at the *p* position. Optional you can also specify the maximum length to be used.

Every time you add a new function that requires parameters, a new window will be opened where you can configure these attributes.

![](./media/images_image90.png)

Optional parameters will be omitted, as long as their value is either 0
for numbers or empty for strings.

You can also change these parameters if you choose **Edit parameters** in the function drop down list.

![](./media/images_image91.png)

#### Connections

Connections between two tables are displayed as direct line between them.

Create them by dragging one connection point to the other:

![](./media/images_image92.png)

Remove them by clicking on the connection point:

![](./media/images_image93.png)

#### Create a new link

To create a new link select (![J:\\GUI
Design\\Icons\\Menu\\UniversalReporter\\NewLink.png](./media/images_image94.png)) **New link** from the Links
configurator toolbar and enter a link name. The Links configurator
window will open.

Take the attributes needed for the link from the respective application window. This is done by selecting the needed attribute from the attributes tree and dragging it to the Links configurator window. If a second attribute is needed to define one link expression then drag the additional attribute directly on the first one. Both attributes then will be displayed in one box and will be concatenated in the link expression.

> If a needed attribute is not displayed in the attributes tree, you can add it with right mouse click on the table name and selecting **Add attribute**.

In the links configurator always the original attribute names are used. For renamed attributes (when a glossary is used), the name displayed in the tree can be different from the name displayed in the links
configurator.

After selecting the attributes, add predefined SQL functions if needed and define the connection like described above. The first row of the link then will be displayed in the grid.

If an additional row is need for the link then repeat the steps described before.

> If others than the predefined SQL functions are needed, the SQL statement can be modified in the Attribute fields of the grid.

However, in this case the modified cell will be coloured to show that he expression cannot be interpreted by the Links configurator and the  graphical display will disappear.

With (![J:\\GUI
Design\\Icons\\Menu\\UniversalReporter\\SaveLink.png](./media/images_image95.png))
**Save link** the new link will be saved to the links.mdb file which by default can be found in the database folder of the program.

### Creating multi-application windows with two applications

Open a project from one application -- e.g.PID_DB.MDB for Smartplant P&ID. Open a project from another application -- e.g.Madison.mdb for SmartPlant3D.

Tile the windows e.g. using the <img src="./media/images_image87.png" width="27" height="21"> **Tile windows horizontally** icon.

<img src="./media/images_image96.png" width="683" height="370">

Drag an object from one project to another object on the other project.

<img src="./media/images_image97.png" width="670" height="309">

If the object link is not available in the link.mdb file, the following
error will occur.

![](./media/images_image98.png)

However if link is available, the following will appear. For example try to link \[Piping\] object to \[InlineComp_Prun\] Object:

![](./media/images_image99.png)

![](./media/images_image100.PNG)

From this window the user can decide to combine the data so that both datasets are displayed next to each other in the MultiWindow or to create the Comparison (Rule) Window which will show the differences between the original windows based on the link attribute and based on attributes having the same name or on attributes mapped to each other.

**Combine data** will create the **MultiWindow.**

**Show differences** will create the **ComparisonWindow**

> If **Show different rows only** is also selected then items which have not differences (e.g. Tag and displayed properties are the same) will not be listed

In the **Link selection** dialogue box, choose the link you wish to establish from the **Available links** field. You can view the link information using the **Show link information** button.

The link information shows which attributes on the first application will be mapped to which attributes on the second application.

Also select the **Join criteria** required.

The **Keep grouping** functionality influences the number of data rows that will appear in the multi-application window. If rows are grouped in the data grid of the parent windows, it depends on this function whether data will be ungrouped or not, before being linked to create the multi-application window.

> To link grouped data, the link attributes have to be unique with respect to the grouped rows to avoid
cross linking.

> The Multiwindows keeps the filters which are applied in the single windows

> The Monikers get their name from the application of the ofiginal (single) window (for example PID for the application PID, 3D for the 3D application etc).

If 2 PID snapshots are compared then the monikers will be called PID1, PID2 etc.

The **Keep grouping** checkbox is disabled if the selected link requires attributes that are not selected in the attribute panels of the **parent windows** or if further applications have been added to a
multi-application window.

Result of **Combine data** :

<img src="./media/images_image101.png" width="743" height="294">

> The new window will only display attributes selected in the **attributes panel** from its parent tables (from which the join was first created). The sequence of attributes in the **data** **grid** is according to the sequence in the parent windows.

For renamed attributes, the names are taken from the parent windows. Glossaries for multi-application windows are generated temporarily. This means that attributes can be renamed there but the name is only kept as long as the multi-application window is open.

A button called "Match Columns by Name" is available: it will arrange columns with the same (internal mdb column name) next to each other

![](./media/images_image102.png)

This principle is valid for the following type of comparisons:

\- 2 snapshots of the same project and application (for instance PID Rev 1 versus PID Rev 2)

\- 2 different projects of the same application (for instance PID Plant 1 versus PID Plant 2)

\- any application/project versus an XLS list (for instance PID Rev 1 versus XLS LineList)

### Add further applications to multi-application window
{#add-further-applications-to-multi-application-window .list-paragraph}

Further applications can be added to the multi-application window, which was created like described before. Drag an object from the application to be added to the **Multi window table**.

Additional applications will be included via Inner Join to the existing data.

### Legend for multi-application window

At the bottom of **UniversalReporter** is the **legend** block for the multi-application window. You can hide/unhide this block by clicking on the **Hide (**![](./media/images_image103.png)**)**/**Unhide
(**![](./media/images_image104.png)**)** icon.

The **legend** block illustrates the source of each attribute by colour. Example below, attributes shaded in green are from the SPPID table and those shaded in purple are from XLS.

![](./media/images_image105.png)

### Context menu for legend block

Context menus for the **legend** block can be activated by a right mouse click on the record which needs changing.

![](./media/images_image106.png)

Changes that can be made:

-   **Rename moniker:** the system automatically generates a moniker name, which is added to the attribute names to indicate the related application. This can be changed manually. Select **Rename moniker** and type in the new name.

> Renaming Monikers as described before is used to assign a display name and will not change the column name in the database. To change the column name the renaming has to be done in the Legend of **Link selection** window, before the multi-application window is created (see *Creating multi-application windows with two applications*)

-   **Change colour:** the system usually auto-selects a colour code for each moniker. This can be changed. Use **Change colour** and select the colour you want.

Select the colour you want. The result is as follows:

![](./media/images_image107.png)

-   **Show link information:** this will show the tables the multi-window table is based on.

-   **Reset moniker name:** resets moniker name to its original value.

### Working with multi-application window

Multi-application windows provide the same reporting functionality as windows for one application.

Additionally, there are some functions to display their relations to the windows on which they are based:

With a multi-application window active, the **Show related windows** button can be used to arrange the multi-application window together with all the related windows, tiled vertically.

Double clicking a row in a multi-application window will create additional views in all related single application windows. These views in the single application windows, called multi-view filter, display only data of the item that was selected in the multi-application window. This is helpful if data of that item have to be edited (see **Edit
functionality**) which is not possible in a multi-application window.

> If the grid in a multi-application window contains columns having the same name except for the monikers,
then buttons (![J:\\GUI
Design\\Icons\\Menu\\UniversalReporter\\ReOrderColumns.png](./media/images_image108.png)) **Match columns by name** and
(![J:\\GUI
Design\\Icons\\Menu\\UniversalReporter\\DeleteIdenticalColumns.png](./media/images_image109.png)) **Hide matching columns without differences** can be used to rearrange or hide these columns (see *Working with rule data window*).

## Rule data window (Comparison window)

The **RuleWindow** can be created from the **MultiWindow** (as described in the chapter **Apply the Auto compare rule to the multi-application window**) but it can also be created directly from the combination of the original windows be selecting **Show differences** button (in this case the colour-coded difference report will be delivered as result).

### Create the difference list report directly from the original windows

By choosing "Show differences" the data from the single windows is shown in one new window displaying the colour-coded differences. The MasterTag attribute is created based on the link which was defined to compare the data in the 2 windows.

![](./media/images_image110.png)

### Apply the Auto compare rule to the multi-application window

An **Auto compare** rule is available in a multi-application window, using right mouse click on **Multi Window Table** and selecting **Apply rule →Auto compare**. This rule compares all columns which have identical column names in the single-application windows the multi-application window was created from. The result of this comparison will be displayed in a new window, the rule data window.

<img src="./media/images_image111.png" width="755" height="434">

The user can decide whether to get all rows (**Show all rows**) or rows with differences only (**Show different rows only**), in the appearing **Rule data** window. Differences are highlighted in various colours, with corresponding columns having the same colour.

> In the Multi-Window and also in the Rule Window it is possible to use the Highlight function for PID objects (assuming the drawings are in the corresponding Igr folder).

With button (![J:\\GUI
Design\\Icons\\Menu\\UniversalReporter\\ReOrderColumns.png](./media/images_image108.png)) **Match columns by name**, columns with
the same name, i.e. columns with the same colour, will be sorted
together. With button (![J:\\GUI
Design\\Icons\\Menu\\UniversalReporter\\DeleteIdenticalColumns.png](./media/images_image109.png)) **Hide matching columns without
differences** all columns with matching name that do not have different values (that is columns without coloured fields) will be hidden.

The result is a window displaying the differences colour-coded:

![](./media/images_image110.png)

The result can be exported to a Spreadsheet. 

Difference list created without template:

![](./media/images_image112.png)

Difference list created with template (for reports created from the AutoCompare window it is possible to select the "Export grid with colour" option):

![](./media/images_image113.png)

<img src="./media/images_image114.png" width="760" height="243">

### Working with rule data window

Rule data windows provide the same reporting functionality as single-application or multi-application windows.

Additionally, there are some functions to update the single-application windows, the rule data window is based on.

> If the multi-application window which was used to apply the rule, was created using the **Keep grouping** option, then these update functions are not available.

Every rule data window, created with the **Auto compare** rule, contains a column named **MasterTag**. When comparing data of two applications in the rule data window, the user can decide, by row, which application contains the correct data and can mark the rows with a so called MasterTag, which defines the leading application. Right mouse click on selected rows provides the functions **Assign master tag for selected rows** and **Assign master tag for all rows** combined with the names of the related single-application windows. Thus the user can assign the name of the window which has to become the master. To delete a master tag, assigned before, **Nothing** has to be selected for **MasterTag**.

![](./media/images_image115.png)

The **MasterTag** column will display all assignments already made. If, for example, one single-application window is PID1 data and the other one is PID2 data, then the situation might be as shown below:

With **MasterTag** assigned, the single-application windows can be updated from rule data window with **Rule → Update all columns** or **Rule → Update columns from grid only**.

![](./media/images_image116.png)

Updating in this context means, that columns in one single-application window will be updated with values from corresponding columns of the other single-application window. In the example above, all rows with **MasterTag** PID2::PID will use PID2data to update rows in PID1 data window.

![](./media/images_image117.png)

> Update will be done for corresponding columns only, that means for columns having the same name in both single-application windows.

After update, the single-application windows will show the changed values highlighted in blue, exactly as if they had been changed manually (see *Edit functionality*). If a Column **ChangeStatus** is available, the updated rows will be marked in this column as \"updated\". In addition to updating existing data, the update functionality inserts rows, which exist in the master application but do not in the
application to be updated. If **ChangeStatus** column is available, then these new rows will be marked there as \**inserted**\". With **ChangeStatus** column available it is also possible to mark rows as \"deleted\" which do not exist in master application but do exist in the application to be updated.

After update, all rows of rule data window, that had been executed during update, will be marked in MasterTag column, e.g. with **Updated from PID2::PID** or **Updated from PID1::Xls**.

**Update all columns** executes the update for all corresponding columns in the selected tables, independent of which columns are displayed in the data grid of the single-application window or rule data window. With **Update columns from grid only**, the update is restricted to columns having the same name and being selected in rule data window. Insert will always be done for all columns.

In some special cases, corresponding columns can be excluded from update, even if they are selected in rule data window. So called
optional columns, which can be defined for extraction in **ReportAdapter for Microsoft Excel** (see *Manual_ReportAdapter for Microsoft Excel*),
can be excluded from update by selecting **Rule → Update all columns** **→** **Without optional columns** or **Rule → Update columns from grid
only →** **Without optional columns.**

<img src="./media/images_image118.png" width="714" height="207">

<img src="./media/images_image119.png" width="714" height="207">

> **With** or **Without optional columns** functionality is only available if optional columns were defined during extraction with **ReportAdapter for Microsoft Excel**.

## Open related drawings (3D and PID only)

Use the context menu **Highlight (\...) objects in (\...) drawings\...**
(PID) and **Open \... matching ISO files** (3D) respectively to open any
related IGR or ISO files.

> To select more than one object, click on the headers of the needed rows, using the Ctrl or Shift key. Then call the context menu by right clicking on one cell of the selected rows.

If these context menus show no drawings, the folders IGR and ISO might be missing.

Using **Tools** menu **Highlight changed values** (PID EditAdapter
only), drawings with changed values will be displayed. **Tools** menu
**SP_ID/Representation_ID\...** (PID only) enables the user to search
for SP_ID or Representation_ID in P&ID drawings.

## Highlight OPC differences (PID only)

**Tools** menu **Highlight OPC differences** toggles marking of paired OPC attributes which are different**.**

For paired OPCs all Piperun and PlantItem attributes are compared and all inconsistencies in these attributes are marked in red. For OPCs which cannot be compared, because the partner is in stockpile or is not displayed in the **data grid**, the OPCTag is marked.

![](./media/images_image120.png)

The list can be exported to Microsoft Excel keeping the red field with **Tools** → **Export to Excel**→ **Export grid with colour** (see *Reporting*).

## Add documents

Use the context menu item **Add documents** to attach a document or a document link to a node. Documents will be stored in the folder as specified in **Settings** (**Path to data files**). Nodes with attached documents are marked in blue.

Instead of adding documents manually, an Excel file can be used. Simply use an Excel file like this (column order and headers in row 1 are obligatory).

![](./media/images_image121.png)

## Create Access query from attributes

Creates an additional query in the database which contains all attributes that are currently displayed on the grid.

## Query builder (Mat only)

### Creating new query

In **UniversalReporter**, you can create your own object by linking several objects together. This will help you create complex reports with data from several objects linked together. To use the query builder, the steps are:

From the main menu, select **Tools → Query builder\...**, this will open
the **Query definition** dialogue box.

![](./media/images_image122.png)

Afterwards, select a query from **Existing queries** or click on the **New** button to create a new query. The **New query** dialogue box opens.

![](./media/images_image123.png)

Enter the name of the new query in the **Query name** field.

Select a **Base table**.

Select table(s) associated to the base table.

Click the **OK** button. The information returns to the **Query definition** dialogue box.

![](./media/images_image124.png)

To add attributes from the base and associated tables, expand the tables
by clicking on the + sign.

![](./media/images_image125.png)

Select the required attributes in the **Associated tables** field and click the![](./media/images_image126.png) button. This will move your selection into the **Query columns** field.

Click **Save** when ready. The new object is now available in the **attributes tree** for use.

### Deleting query

To delete a query, open the **Query definition** dialogue box (**Tools → Query Builder**), select the query and click on the **Delete** button.

![](./media/images_image127.png)

A warning message will appear. Click the **Yes** button to proceed.

![](./media/images_image128.png)

![](./media/images_image129.png)

### Renaming query

To rename query, open the **Query definition** dialogue box (**Tools → Query Builder**), select the query and click on the **Rename** button.

![](./media/images_image130.png)

A dialogue box will let you enter a new name for the query. Click **OK** and close the query builder. The query in the **attributes tree** will get renamed.

![](./media/images_image131.png)

### Importing query

A user can import a query from another .mdb file. To import a query, open the **Query definition** dialogue box (**Tools → Query builder**), select the query and click on the **Import** button.

![](./media/images_image132.png)

The **import query** dialogue box opens. Click on the **Browse** button to locate an .mdb file from which you want to import the query from. The system will then list all available queries from this .mdb file. 
Select the query/queries required.

Click **Import**.

The imported queries are now available to be added to the **attributes tree.**

![](./media/images_image133.png)

## Edit functionality 
(with EditAdapter licence only)

### Grid colour coding

In order to show the different states of each value in the **data grid**, the cells of the grid are colour coded, when **EditAdapter** licence is installed. There are three colours as seen below:

![untitled](./media/images_image134.png)

Blue represents a cell that has had its value changed. Grey cells are cells that cannot be edited, due to user rights, or belonging to read only values in the **Intergraph a**pplication. White coloured cells represent unmodified changeable values.

User rights for editing SmartPlant 3D data correspond to rights in SmartPlant 3D. For SmartPlants P&ID data user rights can be defined in separate privilege databases (see *Attribute privileges*). Rights for **Microsoft Excel** data can be defined in an Excel mapping sheet (see *Manual_ReportAdapter for Microsoft Excel*).

For SmartPlant P&ID data it is possible to remove the Read-Only flag from additional attributes like FluidCode, ItemTags and DrawingName and Number.

This can be done by changing the value of the parameter "UpdateExtendedAttributes"

In the ini file (.UserProfile\\Appdata\\Roaming\\CAXperts\\Universal Reporter\\PID Data\\

\<PlantName\>.ini the following parameter must be changed (or added of it is not available)

*\[Edit\]\
UpdateExtendedAttributes=1\
\
*0 = default behaviour FluidCode, ItemTag, Drawing Name and DrawingNumber cannot be changed

1 = attributes like FluidCode, FluidSystem, Attributes for PipingConnectPoints can be modified

2 = ItemTag, Drawing Name and DrawingNumber can be modified (also FluidCode and PCP attributes)

### Changing a value

Editing a value in the grid is done very quickly with either the dropdown box (as seen below) or via direct input. Columns that are code listed can only be changed using the dropdown.

![untitled](./media/images_image135.png)

For P&ID data and for 3D data you can use the context menu **Extended modifications** for easier data modification. (see *Grid context menu for PID only*)

After every change, a window shows up and asks how the change should be applied (in the PID and in the 3D settings the prompt can be suppressed for P&ID data by checking the option **Suppress prompt**).

![](./media/images_image136.png)

**Only apply the change to the selected object:** modifies all related rows, if applicable. E.g. if an equipment object has four nozzles, the nozzles will be listed in four rows. If the user changes the equipment name, the equipment name will be changed on all four rows.

**Apply change to filtered values with...:** only values shown in the filter will be changed, not necessarily all related values in the database.

### Display changes

After changing a value, the **Old value** column will appear in front of the changed column. After writing changes back to the original application, the **Update status** column is added behind every column
that has been updated (see below).

![untitled](./media/images_image137.png)

The **Old value** operator shows the original value of a changed attribute. This is to ensure that prior to writing changes to the application it is known which changes have been made.

The **Update status** displays one of four values after the changes have been written to the application. The first of the possible shown values for this column is \"successfully updated\". This simply means that all changes have been recorded without a problem. The second possibility is "Up-to-date". This states that the value that has been changed in the report grid has the same value as the object in the application. The third possibility is an error message. This means that the value cannot be written to the application database. Amongst other things, this can be due to user rights, application rules or database problems. The last of possible values is blank. This means no action has been taken.

The columns **Old value** and **Update status** can be added and removed where applicable, by checking the respective operator in the **Attribute panel**. You can also hide and display these values using the **Toggle old value and update status** button
(![](./media/images_image138.png)). As long as old values are displayed, a **Changed values** filter is available in all columns with changed values.

**Reset values**

It is possible to reset changes values in the grid (as long as they are marked as change).

![](./media/images_image139.png)

Reset table will reset the complete table

Reset column will reset the column

Reset value will reset the single value

## Import from Excel (3D, PID and XLS)

Use **Tools → Import from Excel** and select an Excel report. The **Import from Excel** window will appear.

![](./media/images_image140.png)

**Importing without Import Configuration**

Select **Sheet to import** and **Task to import to** and assign the **Header row line** (row in Excel sheet which includes the attribute
names). After ticking the **Next** button you can select the attributes to be imported, by dragging them from the **Ignore columns** area to the **Update columns** field.

![](./media/images_image141.png)

If the attribute names in the Excel file do not match the names in **UniversalReporter** then they are displayed in red and a mapping has to be done, using the **Map** button which calls an additional mapping window. To map **Mapping columns** to the respective **Unknown columns** just drag the needed attribute from the right side to the unknown column name on the left side. To remove mapping, drag the mapping name from the left side to the right.

![](./media/images_image142.png)

In **Select attributes to change** window, it is not allowed to add read-only or unmapped attributes to the right list; this will bring up an error message after clicking **OK**. You need to drag one or more attributes to the **Key columns** field on the left, to build up a unique key (e.g. OID or ItemTag) that matches the rows to be updated and at least one attribute to the right column. Attributes on the column in the middle will be ignored and thus do not have to be mapped.

Start the import by clicking **OK**.

![](./media/images_image143.png)

If duplicate keys are found in Excel then these will be displayed in a window and the user can choose if he wants to import the data ignoring the duplicate keys or the "last value wins" option (in this case the last row for rows with same key will be used for importing for all the objects identified).

After the import, modifications get blue mark-up on the grid and the column **Old value** will be filled.

For S3D data Code list values will be evaluated during import. Errors during evaluation will cause a rejection of these modifications.

**Importing with using an Import Configuration**

**Creating Import configurations**

An attribute configuration can be created by saving the combination of Key column and Update columns in relation so that they can be used for imports from different Excel files.

![](./media/images_image144.png)

![](./media/images_image145.png)

![](./media/images_image146.png)

When the user selects an ImportConfiguration clicking the "Import" command will directly run the import and the result will be visible in Universal Reporter.

![](./media/images_image147.png)

Parameters for identification of valid Import configurations:

![](./media/images_image148.png)

As marked in the screenshot above the 3 parameters which are used to define valid Import configurations are

\- the name of the Excel sheet in the Excel file

\- the row number of the header row (containing the column headers)

\- the task (table) to which the data will be imported

It is also possible to create a new Import configuration based on an existing one by using the "Save As" button in the ImportConfiguration window:

![](./media/images_image149.png)

![](./media/images_image150.png)

In this case the user can choose between the valid Import configurations:

![](./media/images_image151.png)

**\
**

**Removing an Import configuration**

An Import configuration can be removed by selecting it and clicking on the "Remove" button.

![](./media/images_image152.png)

**Removing all Import configurations**

The "Clean" button will remove all related Import configurations (matching to the Excel Sheet and Target Task / Table) from the database.

![](./media/images_image153.png)

**Renaming an Import configuration**

![](./media/images_image154.png)

**Switching to the import method without Import configuration**

If the user wants to switch to the import method which does not use an Import configuration he can check the "Don't use configuration" check box:

![](./media/images_image155.png)

> Using import configurations makes it possible to import different Excel files with the same configuration (assuming that the Name of the Excel sheet is the same and the Headre row number is the same).

>  When importing without Import configuration the user is prompted to select the key column and the update columns (ev. Also the mapping between Excel columns and Universal Reporter columns) for each new Excel file he wants to import.

> The Import Configurations are stored in the Link.mdb database.

> All attributes assigned to the **Update columns** field will be imported, not only the attributes and objects shown on the **data grid**.

> If the key columns contain duplicate values, you will be prompted to overwrite duplicates, ignore duplicates or cancel the import.

## Writing changes to application

To write changes to an application, this application has to be installed on the computer. From the menus, select the respective application (3D, PID or Xls), e.g. **3D -\> Write changes to SmartPlant 3D.**

![](./media/images_image156.png)

Upon the selection of this option, the **Update attributes tables selection** window appears. Select the tables to be updated from this window

![](./media/images_image157.png)

The changed values of the selected tables will be written to the application, using the application interface. Once this process is complete a message box will appear, stating that data have been written to the application successfully and, in **UniversalReporter**, **Update status** columns will display the update status for every changed attribute.

For P&ID data you can select either to **Write changes to SmartPlant P&ID\...** or to **Write changes to SmartPlant P&ID with system editing\...** This will set the respective option in SmartPlant P&ID.
Thus, with system editing will result in a propagation of the changed values to connected items.

>  When **FluidCode** (or **TestFluidType** from CaseProcess attributes) is changed and written to SmartPlant P&ID the **FluidSystem** value is also changed (regardless if the FluidSystem attribute is available in the mdb); this change is activated in the WriteBack step.

For Excel data, after selecting the tables to be executed, a new window appears where the user can select the Excel file that will be used for write back. Additionally he can assign a revision number and colour which will be used to mark the changes in the Excel file. To assign the revision number to the changed rows in the Excel sheet, this sheet has to contain a column with the name **Revision**.

![](./media/images_image158.png)

> To write data back to Excel, the Excel sheet has to include a key column, named\
**xlsKey**, which contains the unique identifier, and this name has to be mapped to the corresponding attribute in **UniversalReporter** (e.g. ItemTag).

After updating and depending on necessity, it may be necessary to export a new Access database from the application with **ReportAdapter,** e.g.to see which values were changed additionally because of rules in the application.

## Permissions and settings

## Permissions

Assign permission with **Tools -\> Permissions.**

![](./media/images_image159.png)

**Group or user names** contains either the domain and user name, the domain and group name, the computer and user name or for those computers that are not in a domain, the computer and user name.

As long as the setting **Add new users with full permissions** is checked (see *6.3 Global settings*), each new user which logs on to the computer and starts **UniversalReporter** will automatically obtain full permissions.

The permissions path can also be designated in the settings. This way multiple users can utilise the same permissions DB.

Both the **Add new users with full permissions** option and the **Permissions .mdb file** setting require the **All -\> Permissions** permission to change.

Warning: Removing the **Add new users with full permissions** and the **All -\> Permissions** setting for all users will disable any future permission modification (unless the permissions database password is known)

Warning: The program will not start without a valid permissions database.

> Removing the active user in the permissions will only serve to reset his/her permissions (to the settings defined in **Add new users with full permissions**).

### Specific permissions:

-   Attribute_Manager: allows the opening and changing of the attribute manager for the specified program part

-   Attribute_Permissions: allows the editing of editable columns in the Attribute Permissions window (PID Only)

-   Change_Values: allows the editing of values in the data grid (Edit Only)

-   Glossary: allows the viewing and modification of the glossary table

-   Import_from_Excel: allows importing changes from excel sheets (Edit Only)

-   Link_Configurator: allows editing links needed for creating multi-application windows

-   Save_View: allows saving over existing views. If disabled will only allow \"Save as\" dialog and will not allow overwriting of existing files.

-   WriteBack: allows the user to utilize the WriteBack function (Edit Only)

## Attribute permissions (PID only)

Attribute permissions are enabled and disabled in the settings under **PID Settings -\> Enable attribute permissions**. The default is **disabled**.

The directory of the attribute permissions is modifiable using the **Settings -\> PID Settings -\> Attribute permissions directory**. If the directory is empty, new (empty) databases will be created there. These database files can be copied and renamed to define the privileges for additional plants (source and target plant have to have identical attributes).

Locked attributes are shown in grey colour on the grid; for users that are not listed in the privileges database, all attributes will be locked.

> If the Property attributes are enabled and no user has been added, no fields will be editable. Attribute permission settings are stored in per-project databases.

Using **Tools -\> Attribute permission** the modification privileges for attributes can be defined user- or group-wise.

## Global settings

![](./media/images_image160.png)

-   **Open last used files on load:** load the last projects on program start. If check box is not checked the system will require you to manually open the project upon program start-up.

-   **Export report header row count:** set the default for header row for Excel report created without template and for new templates.

-   **Maximum Excel batch row count:** There is a known memory leak in Excel. If you are experiencing any difficulties try to reduce this number. If the number of defined rows is exceeded, the report will be split during creation and merged in the end. This does not influence the result but performance is decreased.

-   **Add new users with full permissions:** if checked, new users will get full administrator rights. see permissions

-   **Permissions .mdb file:** path to store the permissions database.

-   **Path to glossary file:** set the location of the glossary .mdb file. If a global path is set for glossary then the glossary is used for all applications. This means that table names have to be unique
    across applications. If there are duplicate table names, the glossary path must be set for every application separately.

-   **Select all attributes by default:** when checked, attributes in the **attribute panel** will be selected when an object gets added to the **attributes tree.**

-   **Path to data files:** set the default location of the document files which can be attached to nodes of the attributes tree. (*see Appendix B: context menus*)

-   **Use one configuration:** configuration settings are identical for
    all projects.

-   **Use one configuration per project name:** configuration settings shall be bound to the project name.

-   **Copy locally from:** if checked it will copy the settings from the
    path specified.

    Configuration file always is used locally (e.g. C:\\Program Files (x86)\\CAXperts\\UniversalReporter\\Config). If a global configuration is needed then the configuration file can be copied from a global location during start-up of the program.

-   **Replace after finish:** if checked the local configuration file will be saved to the path where it was copied from, thus overwriting the global configuration file.

-   **Use one configuration per .mdb file path**: configuration settings shall be bound to the mdb file path (includes a hash value of the file path in the configuration file name).

-   **Links database path**: set the location of the links database (links.mdb).

## Settings per application

### PID settings

![](./media/images_image161.png)

-   **Use global settings:** To override global settings for this application, uncheck this box.

-   **Enable history logging:** if checked, all changes done in SmartPlant P&ID with write back function will be documented in an Access database.

-   **History database file:** defines the location of the change history database

-   **Enable attribute permissions**: if checked, attributes are locked according to user rights specified in the attribute permissions database (see *Attribute permissions (PID only)*). Every project has its own attribute permissions database. If **enable attribute permissions** is set, user rights must be defined for each project.

-   **Attribute permissions directory**: defines the directory where the attribute permissions databases for all projects are kept.

-   **Highlight colour:** the software will use this colour to highlight objects in SmartSketch

-   **Highlighted line width factor:** the software will use this line width to highlight objects in SmartSketch

-   **Suppress update prompt:** suppresses the prompt that asks for the modification target when a cell value was changed.

-   **Suppress default update selection:** sets how modifications should be applied by default.

Other settings: See *Global settings*.

### Grid settings

![](./media/images_image162.png)

Grid settings allow the user to change the colour for the Read-Only, Old_value, Update value colour and also to choose a different colour for even and odd rows.

### Settings for other applications

-   Use global settings: To override global settings for this application, uncheck this box.

Other settings: See *Global settings* or *PID settings*.

## Appendix A: menu

## File

### Open project...

Shows a file dialogue to open a Microsoft Access database containing the project data.

### Recent projects

Shows a list of the most recently used projects. Click on an item on this list to open the project.

### Open view...

Opens a **UniversalReporter** view file (.cvf).

### New view...

Creates a new view tab.

### Save view

Saves the current view as a **UniversalReporter** view file (.cvf).

### Save view as...

Saves the current view as a **UniversalReporter** view file (.cvf) to a
new location.

### Save all views...

Saves all views as **UniversalReporter** view files (.cvf).

### Close view

Closes the current view.

### Close project

Closes the current project.

### Exit

Closes **UniversalReporter**.

## Edit

### Search in attributes tree

Brings up the **Search in attributes tree** form to search the tree of
attributes.

## Tools

### Links configurator

Shows the **Links configurator** window to create links for multi-application windows. (see *Links* )

### Attribute permissions (PID only)

Shows the attribute permissions window (see *Attribute permissions*).

### Attribute manager

Shows the attribute manager window, see *Customising the attributes panel*.

### Glossary

Opens the project specific glossary, see *Customising attributes names (glossary function)*.

### Report variable definition

Shows the **Report variable definition** window to define variables that can be used in Excel reports (see *4.4 Reporting*)

### Custom sorting

Shows the **Sort** window to sort multiple columns in the **data grid** (see *Filtering and sorting views*)

### Create Access query from attributes

Creates a query including all attributes displayed in the data grid and saves it to the current database. (see *4.12 Create Access query from attributes*)

### Export to Excel

Creates Excel report, see *Exporting to Microsoft Excel*.

### Export to HTML, PDF, CSV

Creates report in HTML, PDF or CSV Format from the data displayed in the data grid. With this functionality no template can be used.

### Load documents from Excel

Assigns document links, provided in an Excel sheet, to items in the attributes tree. See *Appendix B: context menus / Add document).*

### Show info

Opens a table which displays information related to the database that contains the extracted data. This information may contain an expiration date. If this date is exceeded a warning will be displayed whenever the
database is opened.

### Remove read-only from view

If a view is locked, i.e. read-only, clicking this menu item will remove the lock. (*see Views*)

### Import from Excel (3D and PID only)

Imports data from an Excel Sheet to the current database. (see *5.4 Import from Excel*)

### Permissions

Shows the **Permissions** window where user rights in **UniversalReporter** can be defined. (see *6.1 Permissions*)

### Settings

Includes global settings and setting for the different applications. (see *6 Permissions and settings*). All settings will be stored in .ini files. (e.g. in C:\\Program Files (x86)\\CAXperts\\UniversalReporter\\Config)

## Application specific menus
(PID, 3D, Mat, Xls, Rule)

### Highlight changed values
(PID only)

Opens all drawings with items to be changed and highlights these items. (see *Open related drawings (3D und PID only)*)

### Highlight SP_ID or Representation_ID (PID only)

Brings up the **Highlight values** window to search for SP_ID or Representation_ID in all P&IDs. (see *Open related drawings (3D und PID only)*)

### Highlight OPC differences (PID only)

Toggles marking of paired OPC attributes which are different. (see *4.10 Highlight OPC differences (PID only)*)

### Write changes to SmartPlant P&ID with system editing (PID only)

Writes changed values back to SmartPlant P&ID using the system editing functionality. (see *5.5 Writing changes to application*)

### Write changes to SmartPlant P&ID (PID only)

Writes changed values back to SmartPlant P&ID. (see *5.5 Writing changes to application*)

### Show history (PID only)

If that option is selected in the settings, all changes that were made ​​in writing back to SmartPlant P&ID get stored in a database. This change history database will be opened and displays the data concerning the current project.

### Write changes to SmartPlant 3D (3D only)

Writes changed values back to SmartPlant 3D (see *Writing changes to application*)

### Show database information (Mat only)

Opening **UniversalReporter for SmartPlant® Materials** normally triggers the following dialogue box. This dialogue box gives you
information on the current database the system is linked to. See sample below:

![](./media/images_image163.png)

### Query builder (Mat only)

Helps to create your own object in the database by linking several tables or queries from the database together.

### Write changes to Xls (Xls only)

Writes changed values back to Microsoft Excel (see *Writing changes to application*)

### Update all columns (Rule only)

Updates single-application windows out of rule window (see *Working with rule data window*) including all columns having the same column name in both single-application windows.

### Update columns from grid only (Rule only)

Updates single-application windows out of rule window (see *Working with rule data window*) including columns displayed in the grid and having the same column name in both single-application windows.

## Window

### Cascade

Cascades the project windows.

### Tile horizontally

Tiles the project windows horizontally.

### Tile vertically

Tiles the project windows vertically.

### Show related windows

Only active when a multi-application window is selected. Arranges the
multi-application window together with all related windows.

### Show attributes tree

Shows / hides the **attributes tree**.

### Show attributes

Shows / hides the **attributes panel**.

### Show grid

Shows / hides the **data grid**.

### Window toolbar

Shows / hides the window toolbar.

### Views toolbar

Shows / hides the view toolbar.

## Help

### Manual

Show this manual.

### Labs

Provides a list of labs in which examples of how to use
UniversalReporter are described step by step.

### Update licence

Shows the licence update form, to update licences or to change license
path. (see *Licence*)

### About UniversalReporter

Displays version information of **UniversalReporter**.

## Appendix B: context menus

There are context menus for each section of the **UniversalReporter**. The menus can be reached via right clicking on the associated item. Some context menu functions are also available on the menu bar and/or toolbar.

## View context menu

The view menu shows up when you right click on the view tab:

![](./media/images_image164.png)

-   **Save view:** saves the currently selected view. If the selected view has not been saved before, the **Save as** dialogue will be opened.

-   **Save view as:** saves the view under a certain file name.

-   **Close view:** closes the selected view.

## Node context menu

In the **attributes tree**, depending on what item you right click on a slightly different context menu will appear, see *Attributes tree*.

### Object nodes

If you right click on the **object node**, the following context menu will appear:

![](./media/images_image165.png)

**Add attribute:** opens a pop-up menu listing all attributes which are not already added. Select one to add it to the selected node.

**Add all attributes:** add all available attributes to the selected node.

**Remove all attributes:** remove all attributes assigned under the selected node.

**Add table:** opens a popup menu listing all tables which are not already added. Select one to add it to the **attributes tree**.

**Add all tables:** add all available tables to the **attributes tree**.

**Remove table:** remove selected node from the **attributes tree**.

**Remove all tables:** remove all tables from **attributes tree**.

**Export table\...**: Exports the selected object to an external
database. The object can be renamed while saving it.

**Add document (in single-application windows only)**: attaches a document to the selected item (see *Add documents*).

**Documents (in single-application windows only)**: displays all attached documents. Open the document by selecting the document name.

**Remove document (in single-application windows only)**: displays all attached documents. Remove the document by selecting the document name
or remove all documents by selecting **All**.

**Apply rule → Auto compare (with multi window table only)**: Starts the auto compare rule (see *Apply rules to multi-application window*), available only in multi-application window, with Rules licence.

### Attribute node

If you right click on the **attribute node**, the following context menu will appear:

![](./media/images_image166.png)

**Rename:** renames the target attribute node. Only works if a glossary is selected under **Tools → Glossary**.

**Reset rename:** reset the renaming of an attribute back to the database column name.

**Add attribute:** opens a popup menu listing all attributes which are not already added. Select one to add it to the selected node.

**Add all attributes:** add all available attributes to the selected node.

**Remove attributes:** remove the selected attribute.

**Add document, Document** and **Remove document**: *see Object nodes*

### Value node

If you right click on the **value node**, the following context menu
will appear:

![](./media/images_image167.png)

**Add attribute:** opens a popup menu listing all attributes which are not already added. Select one to add it to the selected node.

**Add all attributes:** add all available attributes to the selected node.

**Remove all attributes:** remove all attributes from the selected node (not possible for value nodes).

**Add document, Document** and **Remove document**: *see Object nodes*

## Attribute context menu

Right click on the **attributes** panel to open the **Attribute context menu** or use the respective icons.

![](./media/images_image168.png)

Actions available are as follows:

-   **Select all (**![J:\\GUI Design\\Icons\\Menu\\3D DataManager\\CheckAll.png](./media/images_image169.png)**):** selects all attribute nodes

-   **Unselect all(**![J:\\GUI Design\\Icons\\Menu\\3D
    DataManager\\UncheckAll.png](./media/images_image170.png)**):** unselect all attribute nodes, including operator nodes.

-   **Toggle old value and update status (**![J:\\GUI Design\\Icons\\Menu\\3D
    DataManager\\ToggleRowCounter.png](./media/images_image171.png)**):** (only with **EditAdapter**
    licence) Shows / hides **old value** and **update status** columns in the **data grid.**

> Unselecting will not affect the path attributes.

## Grid context menu

**Context menu for filtering**

The context menu shown below is the first and primary way to filter (see *Filtering and sorting views*). Right click on a grid cell to open it.

![](./media/images_image172.png)

**Filter by selection:** sets the column filter to the current value in the selected grid cell.

**Exclude selection:** sets the current column filter to exclude the value in the selected grid cell.

**Append exclude selection:** excludes the current value in addition to the current value(s) in the filter. This option is only enabled if the column has been filtered by exclusion.

**Filter by list:** displays a list where all values to be used for filtering can be selected.

**Copy to clipboard:** copy the displayed name or glossary name of the current column to the clipboard.

**Operational filters:** these filters are only available for numeric and date/time columns. The popup menu contains filters for 'Greater than', 'Less than', 'Greater than or equal to', and 'Less than or equal to', all are relative to the current selected grid cell.

**Append operational filters:** these filters are only available for numerical or date/time columns and if a previous operational filter has been set in the current column. This option enables the possibility to filter for values between two values.

**Compare similarities with:** compare values between two attributes for similarities. This is not possible between different data types.

**Compare differences with:** compare values between two attributes for differences. This is not possible between different data types.

**Extended modifications/Concatenate (PID and Xls only)**: not available for locked cells. Opens the **Concatenate** window to concatenate all
values of items included in the selected column with additional strings.

**Clear column filter:** clears the current column filter in which the selected grid cell resides.

**Clear all filters:** resets all filters in the grid. With the
exception of filters set in path columns (see *Data grid*).

It is also possible to filter by selecting an item from the filter combo
box at the top of each column (see *Filtering*).

## Application specific context menu

### Grid context menu for 3D only

**Open (\...) matching ISO files**: opens isometric drawings related to
the selected item, (*see Open related drawings (3D and PID only))*. Only
available if drawings were extracted with **3D ReportAdapter** and
folder \"ISO\" exists. If there are no isometric drawings then **Iso
information missing** is displayed in the context menu.

### Grid context menu for PID only

**Highlight (\...) objects in (\...) drawings**: opens P&IDs related to the selected item see Open related drawings *(3D and PID only).* This option is only available if P&IDs were extracted with **PID
ReportAdapter** and the folder \"IGR\" exists. If there are no isometric drawings then **No drawings** is displayed in the context menu.

### Grid context menu for PID and Xls only

**Extended modifications/Concatenate**: not available for locked cells.
Opens the **Concatenate** window to concatenate all values of items
included in the selected column with additional strings.

**Extended modifications/Regular expression**: not available for locked
cells. Opens the **Regular expression** window to change all values of
items included in the selected column based on regular expressions.

**Extended modifications/Replace**: not available for locked cells.
Opens the **Replace** window to change all values of items included in
the selected column by replacing parts of these values.

### Grid context menu for EditAdapter licence only

**Reset table:** Reset the values of all columns to the old value. Only
available if at least one cell value is different from old value.

**Reset column:** Reset all values of the selected column to the old
value. Only available if at least one value in the column is different
from old value.

**Reset value:** Reset the value of the selected cell to the old value.
Only available if the cell value is different from old value.

### Grid context menu for Rule data with Rules licence only

![](./media/images_image173.png)

**Assign master tag for selected rows:** Assign to all selected rows
which application is the master for updating single-application windows
out of Rule data window. Only available with column MasterTag.

**Assign master tag for all rows:** Assign to all rows which application
is the master for updating single-application windows out of Rule data
window. Only available with column MasterTag.

## Appendix C: toolbar buttons

There are several toolbars in **UniversalReporter.**

![](./media/images_image174.png)

The toolbar allows faster access to the most important functions from
the menu.

Windows toolbar

-   ![](./media/images_image175.png): cascade windows.

-   ![](./media/images_image176.png): tile windows horizontally.

-   ![](./media/images_image177.png): tile windows vertically.

-   ![](./media/images_image178.png): Show related windows

-   ![LeftArea](./media/images_image179.png): shows or hides the **Attributes tree.**

-   ![MiddleArea](./media/images_image180.png): shows or hides the **Attributes panel**.

-   ![RightArea](./media/images_image181.png): shows or hides the **Data grid**.

...

Edit toolbar

-   ![](./media/images_image182.png): search in Attributes trees.

    ...

Views toolbar

-   ![NewView](./media/images_image26.png): adds a new view.

-   ![OpenView](./media/images_image25.png): opens an existing view.

-   ![SaveView](./media/images_image28.png): saves current view.

-   ![DeleteView](./media/images_image27.png): closes the selected view.

-   ![Export2Excel](./media/images_image54.png): exports the report currently shown
    in the grid into a Microsoft Excel worksheet.

-   ![](./media/images_image183.png): custom sorting.

-   **AND** / **OR**: toggle button to switch between "AND" mode and an "OR" mode for filtering

Links configurator toolbar (see *Links*)
![](./media/images_image184.png)

-   ![E:\\Devel\\UniversalReporter\\UniversalReporter\\Resources\\NewLink.png](./media/images_image94.png): creates a new link.

-   ![E:\\Devel\\UniversalReporter\\UniversalReporter\\Resources\\DeleteLink.png](./media/images_image185.png): deletes the link.

-   ![E:\\Devel\\UniversalReporter\\UniversalReporter\\Resources\\SaveLink.png](./media/images_image95.png): saves the link.

-   ![E:\\Devel\\UniversalReporter\\UniversalReporter\\Resources\\RenameLink.png](./media/images_image186.png): renames the link.

-   ![ReorderView](./media/images_image187.png): reorders all link windows

-   ![](./media/images_image188.png): drop down list of all defined links

## Status bar and log file

A double click on the status bar at the bottom of the window will open the log file.

<img src="./media/images_image189.png" width="742" height="472">

The log file can be found in the **UniversalReporter** folder of the temporary directory of the current computer.

---

# UniversalReporter — S3D2PDS

# S3D2PDS

**S3D2PDS** is an add-on for CAXperts **3D ReportAdapter** used to create **PD_XPDA** backup files containing models and metadata.

<img src="./media/Howto create view styles in S3D_image1.jpg"  width="688" height="218"> 

## Workflow

<table>
<tr><td rowspan=2>

</td><td>

Intergraph **Smart 3D** needs Bentley **MicroStation** to export DGN files (without metadata). **S3D2PDS** triggers **Smart 3D** to export DGN files and adds metadata to the result. The quality of exported graphics will be exactly as good as in the DGN files created manually out of **Smart 3D**.

Intergraph **Smart 3D** requires Bentley **MicroStation** in order to export DGN files without metadata. **S3D2PDS** triggers **Smart 3D** to export the DGN files and then adds metadata to the result. The quality of the exported graphics is exactly the same as that of DGN files created manually from **Smart 3D**.

</td></tr>
<tr><td>

The metadata, that is, the attributes, can be configured by using **3D ReportAdapter** as described in the **3D ReportAdapter** manual. Attributes can be selected from all tasks available in **3D ReportAdapter**.

</td></tr>
</table>

## Prerequisites

To run **S3D2PDS**, a DGN export with specific settings must be performed once in **Smart 3D**. To export a DGN file from **Smart 3D**, the user must:

-   Install 7-Zip, available from <http://www.7-zip.org>

-   Create a filter

-   Copy a delivered View Style to the SymbolShare

-   Copy some delivered Graphic Rules to the SymbolShare

-   Create drawing components

-   Create a drawing volume

For **S3D2PDS**, this must be done only once per plant. All items in the list should be named `3D ReportAdapter`. Afterwards, **S3D2PDS** modifies the filter and triggers drawing generation as required.

First, install the graphic rules supplied with **S3D2PDS** on your system. Simply copy the XML files to the relevant folder. These rules ensure that tasks and aspects are mapped to the correct layers according to the **Layer designations in DGN-Output** table at the end of this document.

## Create a filter

In **Smart 3D**, go to **File** → **Define Workspace**

<img src="./media/Howto create view styles in S3D_image8.png"> 

Select **Filter: Create a New Filter**

<img src="./media/Howto create view styles in S3D_image9.png">

Change the **Name** to **3D ReportAdapter**:

<img src="./media/Howto create view styles in S3D_image10.png">

Select a single subsystem:

<img src="./media/Howto create view styles in S3D_image11.png">

Press **OK**.

Now back in the **Define Workspace** window select **Filter: More...**

<img src="./media/Howto create view styles in S3D_image12.png">

Drag and drop the filter **3D ReportAdapter** from **My Filters** to **Plant Filters** to create a plant filter:

<img src="./media/Howto create view styles in S3D_image13.png"> <img src="./media/Howto create view styles in S3D_image14.png">

Press **Cancel** to close the **Select Filter** window.

Press **Cancel** to close the **Define Workspace** window.

## Copy Graphic Rules and ViewStyle to SymbolShare

Copy the delivered Graphic Rules from 3D ReportAdapter Installation (C:\\Program Files\\CAXperts\\3D ReportAdapter\\Templates) into the appropriate place on SymbolShare:

Graphic Rules:

<img src="./media/Howto create view styles in S3D_image15.png">

WrapperReplacementRules (ControlPoints.xml):

<img src="./media/Howto create view styles in S3D_image16.png">

View Style (3D ReportAdapter.xml):

<img src="./media/Howto create view styles in S3D_image17.png">

The view style and graphic rules must be available for correct generation of the DGN output.

## Create drawing components

Now go to the **Task** → **Space Management**:

<img src="./media/Howto create view styles in S3D_image18.png">

Open the drawing console by using **Tools** → **Drawing Console**:

<img src="./media/Howto create view styles in S3D_image19.png">

Right click the root node and select **New** from the context menu:

<img src="./media/Howto create view styles in S3D_image20.png">

Select Folder and press **OK**:

<img src="./media/Howto create view styles in S3D_image21.png">

Rename the new folder to **3D ReportAdapter**:

<img src="./media/Howto create view styles in S3D_image22.png">

Right click on the 3D ReportAdapter node and select **New** from the
context menu:

<img src="./media/Howto create view styles in S3D_image23.png">

Select Microstation 3D DGN and press **OK**:

<img src="./media/Howto create view styles in S3D_image24.png">

Rename **New 3D DGN Drawings** to **3D ReportAdapter**:

<img src="./media/Howto create view styles in S3D_image25.png">

Setup the 3D ReportAdapter node:

<img src="./media/Howto create view styles in S3D_image26.png">

Select the seed file `mmdlsdz` and the style **3D ReportAdapter** by using **More... / Properties**:

<img src="./media/Howto create view styles in S3D_image27.png">

Press **OK**. Close the **Drawing Console**.

## Create drawing volume

Activate the **Engineering Point** tool (<img src="./media/Howto create view styles in S3D_image28.png">).

Use **Set Target to Origin** (<img src="./media/Howto create view styles in S3D_image29.png">).

Use **Place Drawing Volume by Two Points** (<img src="./media/Howto create view styles in S3D_image30.png">).

Select **Drawing Type** More...:

<img src="./media/Howto create view styles in S3D_image31.png">

Select the 3D ReportAdapter drawing:

<img src="./media/Howto create view styles in S3D_image32.png">

Press **OK**.

Select **Space Folder** More...:

<img src="./media/Howto create view styles in S3D_image33.png">

Select the root node and press **OK**:

<img src="./media/Howto create view styles in S3D_image34.png">

Now enter `-1000000, -1000000, -1000000` as the position of the lower coordinate:

<img src="./media/Howto create view styles in S3D_image35.png">

(Do not forget to lock the values by using the lock symbol.)

Click onto the view.

Now enter `1000000, 1000000, 1000000` as the position of the upper coordinate:

<img src="./media/Howto create view styles in S3D_image36.png">

(Do not forget to lock the values by using the lock symbol.)

Click onto the view.

Now select the **Space** tab on the **Workspace Explorer,** right click the volume and select **Properties** from the context menu.

<img src="./media/Howto create view styles in S3D_image37.png">

Change the name to **3D ReportAdapter** and press **OK**:

<img src="./media/Howto create view styles in S3D_image38.png">

## Test run

Finally, test the drawing creation. Go to **Tools** → **Drawing Console**:

<img src="./media/Howto create view styles in S3D_image39.png">

Right click the 3D ReportAdapter node and select **Create Drawings**:

<img src="./media/Howto create view styles in S3D_image40.png">

Right click the new 3D ReportAdapter node and select **Update Now**:

<img src="./media/Howto create view styles in S3D_image41.png">

Now **Smart 3D** opens **MicroStation** to create a drawing. This may take a few minutes. The result should look like this:

<img src="./media/Howto create view styles in S3D_image42.png">

You can then use **Save As** to save the drawing to disk:

<img src="./media/Howto create view styles in S3D_image43.png">

> It is essential that this test run completes successfully.

Save the DGN file, `3D ReportAdapter.dgn`, to disk and check that the DGN level assignment is correct, see **Layer designations in DGN-Output**. This is a prerequisite for the next steps.

## Change of colours in the dgn-output

The colours in the DGN output can be changed by modifying the graphic rules for the relevant objects that are processed on to a specific level in the DGN.

<img src="./media/Howto create view styles in S3D_image44.png" width="728" height="426"> E.g. if you want to change the colour for Piping-Objects on the simple/detailed physical aspect, you have to open and modify the Graphic Rule in the View Style "3D ReportAdapter":

Open the properties of Graphic Rule "3D ReportAdapter Piping_SP3D":

<img src="./media/Howto create view styles in S3D_image45.png">

Here you can select the colour for the aspect of the respective Object-Type:

<img src="./media/Howto create view styles in S3D_image46.png">

These are SmartSketch colours, but during export to DGN the nearest matching MicroStation colour is selected automatically.

**Important:** You must not change the **Layer for Aspect** of the object type.

## Usage of the software

<img src="./media/Howto create view styles in S3D_image47.png">

After the **S3D2PDS** licence key has been installed, a text box called **PD_XPDA backup output file** is shown in **3D ReportAdapter**. Select the output file name there.

1.  Use the standard **3D ReportAdapter** functions to create the attribute database. For information on how to use **3D ReportAdapter** or install the licence file, see the *3D ReportAdapter Manual*.

2.  Press **Extract Geometry** to create a second database containing the per-part DGN files.

3.  Press **Export to PDS** to create a PD_XPDA backup output file containing both graphics and metadata for the selected part of the model, as specified in **Tasks** and **Filters**. In other words, data can also be extracted only partially to the backup file.

## Command line mode

The **ReportAdapter for SmartPlant® 3D** can be used from the command line to automate export of **SmartPlant 3D** data.

To display the **ReportAdapter** batch-mode help, enter the installation path of **ReportAdapter** followed by `\3D ReportAdapter.exe /?`.

`C:\> "C:\Program Files\CAXperts\3D ReportAdapter\3D ReportAdapter.exe" /?`

\"3D ReportAdapter.exe\" \[/plant plant_name\] \[/config config_file.ini\] \[/output output_file.mdb\] \[/task Piping \[/filter area1\]\] \[/task Piping Catalog \[/filter spec1\] \[/filter spec2\]\] \[/force\]

<table>
<tr><td>

{.Definition}/plant plant_name

</td><td>

SmartPlant 3D plant to process

</td></tr>
<tr><td>

{.Definition}/config config_file.ini

</td><td>

Config file which contains the settings for the extraction. If a configuration file is not defined, the last configuration will be used

</td></tr>
<tr><td>

{.Definition}/output output_file.mdb

</td><td>

Output database. If Output database is not defined, the last configuration will be used

</td></tr>
<tr><td>

{.Definition}/task task_name

</td><td>

Task name to extract, followed by the filter. If no tasks are defined, the last configuration will be used

</td></tr>
<tr><td>

{.Definition}/filter filter_name

</td><td>

Filter name, applied to the previously defined task. Multiple filters can be defined for Piping Catalog task

</td></tr>
<tr><td>

{.Definition}/force

</td><td>

Force extraction

</td></tr>
<tr><td>

{.Definition}/pdxpdafile output.zip

</td><td>

PD_XPDA output file.

</td></tr>
<tr><td>

{.Definition}/execute mode

</td><td>

Use PDS0, PDS1 or PDS2 for mode (the number after PDS specifies the geometry filter setting)

</td></tr>
</table>

Sample batch file to export the whole plant `SP3DTrain`:

**\@ECHO OFF**

**\"C:\\Program Files (x86)\\CAXperts\\3D ReportAdapter\\3D
ReportAdapter.exe\" /plant SP3DTrain /force /pdxpdafile
\"c:\\temp\\s3d2pds.zip\" /execute pds0**

**\"C:\\Program Files\\7-Zip\\7z.exe\" e \"c:\\temp\\s3d2pds.zip\"
-o%1**

**REM Missing: Rescale**

**PAUSE**

## Layer designations in DGN-Output

<table>
  <thead>
    <tr>
      <th><strong>Layer</strong></th>
      <th><strong>Task</strong></th>
      <th><strong>Aspect</strong></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>Piping</td>
      <td>Simple/Detailed Physical</td>
    </tr>
    <tr>
      <td>2</td>
      <td>Piping</td>
      <td>MoldedForms (only for SM3D)</td>
    </tr>
    <tr>
      <td>3</td>
      <td>Piping</td>
      <td>Insulation</td>
    </tr>
    <tr>
      <td>4</td>
      <td>Piping</td>
      <td>Operation</td>
    </tr>
    <tr>
      <td>5</td>
      <td>Piping</td>
      <td>Maintenance</td>
    </tr>
    <tr>
      <td>6</td>
      <td>Equipment</td>
      <td>Physical</td>
    </tr>
    <tr>
      <td>7</td>
      <td>Equipment</td>
      <td>MoldedForms (only for SM3D)</td>
    </tr>
    <tr>
      <td>8</td>
      <td>Equipment</td>
      <td>Insulation</td>
    </tr>
    <tr>
      <td>9</td>
      <td>Equipment</td>
      <td>Operation</td>
    </tr>
    <tr>
      <td>10</td>
      <td>Equipment</td>
      <td>Maintenance</td>
    </tr>
    <tr>
      <td>11</td>
      <td>Structural</td>
      <td>Simple/Detailed Physical</td>
    </tr>
    <tr>
      <td>12</td>
      <td>Structural</td>
      <td>MoldedForms (only for SM3D)</td>
    </tr>
    <tr>
      <td>13</td>
      <td>Structural</td>
      <td>Insulation</td>
    </tr>
    <tr>
      <td>14</td>
      <td>Structural</td>
      <td>Operation</td>
    </tr>
    <tr>
      <td>15</td>
      <td>Structural</td>
      <td>Maintenance</td>
    </tr>
    <tr>
      <td>16</td>
      <td>HVAC</td>
      <td>Simple/Detailed Physical</td>
    </tr>
    <tr>
      <td>17</td>
      <td>HVAC</td>
      <td>MoldedForms (only for SM3D)</td>
    </tr>
    <tr>
      <td>18</td>
      <td>HVAC</td>
      <td>Insulation</td>
    </tr>
    <tr>
      <td>19</td>
      <td>HVAC</td>
      <td>Operation</td>
    </tr>
    <tr>
      <td>20</td>
      <td>HVAC</td>
      <td>Maintenance</td>
    </tr>
    <tr>
      <td>21</td>
      <td>Electrical</td>
      <td>Simple/Detailed Physical</td>
    </tr>
    <tr>
      <td>22</td>
      <td>Electrical</td>
      <td>MoldedForms (only for SM3D)</td>
    </tr>
    <tr>
      <td>23</td>
      <td>Electrical</td>
      <td>Insulation</td>
    </tr>
    <tr>
      <td>24</td>
      <td>Electrical</td>
      <td>Operation</td>
    </tr>
    <tr>
      <td>25</td>
      <td>Electrical</td>
      <td>Maintenance</td>
    </tr>
    <tr>
      <td>26</td>
      <td>ControlPoints</td>
      <td>Simple/Detailed Physical</td>
    </tr>
    <tr>
      <td>27</td>
      <td>ControlPoints</td>
      <td>MoldedForms (only for SM3D)</td>
    </tr>
    <tr>
      <td>28</td>
      <td>ControlPoints</td>
      <td>Insulation</td>
    </tr>
    <tr>
      <td>29</td>
      <td>ControlPoints</td>
      <td>Operation</td>
    </tr>
    <tr>
      <td>30</td>
      <td>ControlPoints</td>
      <td>Maintenance</td>
    </tr>
    <tr>
      <td>31</td>
      <td>Space</td>
      <td>Simple/Detailed Physical</td>
    </tr>
    <tr>
      <td>32</td>
      <td>Space</td>
      <td>MoldedForms (only for SM3D)</td>
    </tr>
    <tr>
      <td>33</td>
      <td>Space</td>
      <td>Insulation</td>
    </tr>
    <tr>
      <td>34</td>
      <td>Space</td>
      <td>Operation</td>
    </tr>
    <tr>
      <td>35</td>
      <td>Space</td>
      <td>Maintenance</td>
    </tr>
    <tr>
      <td>36</td>
      <td>Grids</td>
      <td>Simple/Detailed Physical</td>
    </tr>
    <tr>
      <td>37</td>
      <td>Grids</td>
      <td>MoldedForms (only for SM3D)</td>
    </tr>
    <tr>
      <td>38</td>
      <td>Grids</td>
      <td>Insulation</td>
    </tr>
    <tr>
      <td>39</td>
      <td>Grids</td>
      <td>Operation</td>
    </tr>
    <tr>
      <td>40</td>
      <td>Grids</td>
      <td>Maintenance</td>
    </tr>
    <tr>
      <td>41</td>
      <td>NoTask</td>
      <td>Simple/Detailed Physical</td>
    </tr>
    <tr>
      <td>42</td>
      <td>NoTask</td>
      <td>MoldedForms (only for SM3D)</td>
    </tr>
    <tr>
      <td>43</td>
      <td>NoTask</td>
      <td>Insulation</td>
    </tr>
    <tr>
      <td>44</td>
      <td>NoTask</td>
      <td>Operation</td>
    </tr>
    <tr>
      <td>45</td>
      <td>NoTask</td>
      <td>Maintenance</td>
    </tr>
    <tr>
      <td>46</td>
      <td>Supports</td>
      <td>Simple/Detailed Physical</td>
    </tr>
    <tr>
      <td>47</td>
      <td>Supports</td>
      <td>MoldedForms (only for SM3D)</td>
    </tr>
    <tr>
      <td>48</td>
      <td>Supports</td>
      <td>Insulation</td>
    </tr>
    <tr>
      <td>49</td>
      <td>Supports</td>
      <td>Operation</td>
    </tr>
    <tr>
      <td>50</td>
      <td>Supports</td>
      <td>Maintenance</td>
    </tr>
    <tr>
      <td>51</td>
      <td>MoldedForms</td>
      <td>Simple/Detailed Physical</td>
    </tr>
    <tr>
      <td>52</td>
      <td>MoldedForms</td>
      <td>MoldedForms</td>
    </tr>
    <tr>
      <td>53</td>
      <td>MoldedForms</td>
      <td>Insulation</td>
    </tr>
    <tr>
      <td>54</td>
      <td>MoldedForms</td>
      <td>Operation</td>
    </tr>
    <tr>
      <td>55</td>
      <td>MoldedForms</td>
      <td>Maintenance</td>
    </tr>
    <tr>
      <td>56</td>
      <td>StructuralDetailing</td>
      <td>Simple/Detailed Physical</td>
    </tr>
    <tr>
      <td>57</td>
      <td>StructuralDetailing</td>
      <td>MoldedForms</td>
    </tr>
    <tr>
      <td>58</td>
      <td>StructuralDetailing</td>
      <td>Insulation</td>
    </tr>
    <tr>
      <td>59</td>
      <td>StructuralDetailing</td>
      <td>Operation</td>
    </tr>
    <tr>
      <td>60</td>
      <td>StructuralDetailing</td>
      <td>Maintenance</td>
    </tr>
    <tr>
      <td>61</td>
      <td>NoTask</td>
      <td>Simple/Detailed Physical</td>
    </tr>
    <tr>
      <td>62</td>
      <td>NoTask</td>
      <td>MoldedForms</td>
    </tr>
    <tr>
      <td>63</td>
      <td>NoTask</td>
      <td>Insulation</td>
    </tr>
  </tbody>
</table>

---

# UniversalReporter — QualityAssuranceModule

# QualityAssuranceModule

**QualityAssuranceModule** is designed to configure and apply quality checks that help ensure the quality of project data. The **QualityAssuranceModule** is used to check data in `.mdb` files. Quality-assurance filters can be defined in order to check data and display inconsistencies. The results of these checks can be exported to Excel.

## Prerequisites

-   Supported operating systems:
    -   Microsoft Windows XP
    -   Microsoft Windows Server 2003
    -   Microsoft Windows Vista
    -   Microsoft Windows 7 or later

-   Microsoft Office 2003 or later

This is the **menu bar**. It is divided into **File**, **Home**, **Settings**, and **Help**.

This is the **Data Source area**, where `.mdb` files can be selected and opened, and where the views are defined, **Shared Views** and **My Views**.

This is where you see the **Data Grid**.

This is where the **filter definitions** are shown. It is a hierarchical tree, referred to in the manual as the filter tree.

## Open a project

`.mdb` files can be opened by using **Open project** in the data source area.

<img src="./media/QualityAssuranceModule_image9.png" width="357" height="148">

<img src="./media/QualityAssuranceModule_image10.png" width="745" height="462">

The title bar displays the plant name, for example `PPM`, the application name, for example `PID`, and the name of the `.mdb` file. For SmartPlant P&ID, SmartPlant Instrumentation, SmartPlant Electrical, and Smart 3D extractions, all tables defined in the `Task` table in the `.mdb` file are available for selection.

If the Access file was not created by using CAXperts `ReportAdapter`s, only the name of the Access file is displayed in the title bar. In this case, all tables and queries in the Access file can be selected.

## Open a table

To open a table, or switch to another table, click **Table** on the **Home** tab and select the table you want to view. The table names depend on the application, for example `PID`, `INS`, or `3D`, and also on the definitions made in the application `ReportAdapter`s. For more details, see the manual of the relevant `ReportAdapter`.

Double-clicking a table in the data source area opens that table in the data grid.

<img src="./media/QualityAssuranceModule_image11.png" width="1059" height="196">

## Create a view

Views are used to store the specific configuration of tables, such as selected columns, virtual columns, and applied filters.

Views can be stored by using **Save Views as** after right-clicking the table name in the data grid. The user must choose whether the view should be saved in the **My Views** or **Shared Views** folder.

<img src="./media/QualityAssuranceModule_image12.png" width="354" height="146">

<img src="./media/QualityAssuranceModule_image13.png" width="345" height="168">

The paths in which the views are stored are defined by using the **Application settings** button in the **Home** menu.

<img src="./media/QualityAssuranceModule_image14.png" width="869" height="519">

> 1. View files are stored as `.qav` files, Quality Assurance View files, on the file system.
> 2. Rule files are stored as `.qar` files, Quality Assurance Rule files, on the file system.

## Working with the data grid

## Working with views

The changes described below can be applied to views. To save a view, right-click the view and use the **Save view** command.

![](./media/QualityAssuranceModule_image15.png)

An asterisk near the view name is displayed as long as the view has been changed but not yet saved. It disappears after the view has been saved.

![](./media/QualityAssuranceModule_image16.png)

If the user does not want to save the changes, the view can be closed and **No** can be selected when prompted to save the changes.

![](./media/QualityAssuranceModule_image17.png)

## Manage columns

### Order of columns

![](./media/QualityAssuranceModule_image18.png)

The order of columns can be changed by dragging and dropping the column headers with the left mouse button. Click and hold the column you want to move, drag it to the new position, and then release the mouse button.

### Sorting

Click a column header to sort the data grid by that column. Click it again to toggle between ascending and descending order.

![](./media/QualityAssuranceModule_image19.png)

![](./media/QualityAssuranceModule_image20.png)

### Grouping

![](./media/QualityAssuranceModule_image21.png)

You can drag and drop column headers to the dark grey area above in order to group by those columns. The order can also be changed by drag and drop. If you group grid data by a single column, records with identical values in that column are arranged into corresponding data groups. Each data group is identified by a group row, which can be expanded to access its underlying data rows. If you then group the data by another column, the data rows within each group are combined into second-level groups according to the values of the new group column. Using the context menu item **Full expand** on the **GroupBy** attribute shows the grid in expanded mode, as in the example below. Grouping can be cancelled by dragging the attribute back to the list of columns.

### Hide columns in grid

> Hidden columns are not shown in edit panels.

Right-click the column bar and click **Remove this column** or **Column manager**, for multiple columns. To change visibility, check or clear the checkbox in front of the column name.

![](./media/QualityAssuranceModule_image22.png)

![](./media/QualityAssuranceModule_image23.png)

> Hover with your mouse over an entry to see a few values of this column as shown in the screenshot.

### Add virtual columns

A **virtual column** is not bound to any field in a data source. You can provide data for virtual columns by specifying a formula, that is, a string expression, that is used to evaluate values for this column automatically.

Right-click any column name and click **Add virtual column...**

![](./media/QualityAssuranceModule_image24.png)

This opens the **Expression editor**, where you can specify an expression for the column. First, define the name of the new column and its data type. The data type can be selected from a drop-down list.

![](./media/QualityAssuranceModule_image25.png)

The following example shows how to create a virtual column (NPD2). Data
for this column is calculated according to the formula:
Replace(\[Piperun_nominaldiameter\], \'\"\', \' \')

![](./media/QualityAssuranceModule_image26.png)

![](./media/QualityAssuranceModule_image27.png)

> A virtual column must meet the following two requirements:
> 1. Its name must be unique and must not refer to any field in the grid's data source.
> 2. The column's `UnboundDataType` property must be set to an appropriate value, according to the type of data the column is intended to display, for example `Boolean`, `DateTime`, `Decimal`, `Integer`, `String`, or `Object`.

### Edit virtual columns

![](./media/QualityAssuranceModule_image27.png)

Right-click the virtual column you want to edit and click **Edit virtual column...**

### Remove virtual columns

Right-click the virtual column you want to remove and click **Remove virtual column...**.

This command removes the virtual column from the data grid. If the command **Remove Column** is used on a virtual column instead, the column remains available and can be activated later by using the **Column Manager**.

## Search function

![](./media/QualityAssuranceModule_image28.png)

When using the grid search function, only records containing the entered search string are displayed. If the search string is cleared, the grid displays all records.

## Filtering in the data grid

Filtering allows you to display a subset of the records in the data source that meet a particular criterion. When filtering is applied to a grid view, the displayed records are restricted to those that meet the current filter criteria. You can filter data by using a single column or multiple columns. Filtering can be applied by selecting a column's value from the filter drop-down list, by typing directly into the automatic filter row, or by building a filter in the filter editor. For more information about the filter editor, see the chapter *Manage quality assurance rules*.

To display a selection of values that exist in a column, hover the cursor over the column name.
![](./media/QualityAssuranceModule_image29.png)
The icon appears and can then be clicked.

### Filtering using the filter dropdown

Right-click a column and select the filter icon.

You will see a drop-down list of all distinct values in the column. In this list, you can select all values, a single value, or a list of values, as shown in the screenshot below.

![](./media/QualityAssuranceModule_image30.png)

### Filtering using the automatic filter row

The automatic filtering row is displayed at the top of a View and allows data to be filtered on the fly - by typing text directly into a row. If you type text within a row, a filter condition is automatically created based upon the values entered, and this is applied to the focused column.

> Use % as wildcard for multiple characters and _ for a single character.

### Filter expression examples

> To show only records where the field value begins with "12", enter:
> **12**
>
> To show only records where the field value ends with "in", enter:
> **%in**

![](./media/QualityAssuranceModule_image31.png) 
To show records where the field value equals 10, 11, 12, ... 19, enter **1\_**

## Manage quality assurance rules

## Working with quality assurance rules

![](./media/QualityAssuranceModule_image32.png)

Filters can be created directly in the topmost directory or they can be created in folders. We recommend to create application specific filter files (for SPI, SPPID, SP3D, PDS etc.). However it is possible to keep all filters in one file, even if this is not recommended.

> The default folder where the rule definitions are saved is the AppData folder in the users's profile, e.g. C:\\Users\\cr\\AppData\\Roaming\\CAXperts\\QualityAssuranceModule.

> We recommend saving the rule definitions in a central location especially when multiple users will work with the same rule definitions.

### Create quality assurance rule

To create a quality assurance rule, please click on **Add rule** or right click in filter tree and select **Add rule**. After a new filter is created the edit filter panel will be shown.

The Rule Editor allows you to build complex filter criteria with different filter conditions, combined by logical operators. The Rule Editor contains two tabs providing the Visual or Text-based rule editing style.

![](./media/QualityAssuranceModule_image33.png)

#### Rule definition in visual mode

The Rule Editor displays filter criteria as a tree structure, where nodes represent simple filter conditions. If the filter criteria consist of multiple filter conditions, the Rule Editor contains multiple nodes linked by logical operators into groups. You can select And, Or, Not And, Not Or to change the operator. Click
![](./media/QualityAssuranceModule_image34.png) 
to add a condition to your group of operator.

The following example looks for piperuns having NPD 4" or 32".

![](./media/QualityAssuranceModule_image35.png)

In addition to the rule query definition a description and a style (background colour, font) can be defined for each rule (the style will be applied to the grid).

The **Preview** command will apply the filter and style to the grid so that the user can test the result.

![](./media/QualityAssuranceModule_image36.png)

The **style** consists of **BackColour** (colour if the cell in the grid), **Font** (TextFont for the data in the grid) and **ForeColour** (colour of the data in the grid); the style can be applied to one or more columns (ev. also to all columns) in the grid.

**Cancel preview** will remove the filter and style from the grid.

**OK** will save the filter and close the filter editor.

**Cancel** will close the filter editor without saving the filter.

**Rule definition for attributes with select list**

For attributes with select list it is possible to open the content of the select list (right-mouse-click on the value field):

![](./media/QualityAssuranceModule_image37.png)
![](./media/QualityAssuranceModule_image38.png)

This makes it easy to create rules for attributes which have a select list (e.g. check if all pumps have a tag prefix "P" etc).

#### Rule definition in text mode

In text mode the filter conditions are displayed as text; it is mandatory to use this mode for filters that will use functions like **Substring**, **Replace**, **Len** etc.

The next example shows a rule which is checking if the length of the Piping Materials Class does not equal 7 Chars.

![](./media/QualityAssuranceModule_image39.png)

Rules which use functions cannot be displayed in Visual Mode, if the user tries to activate the visual mode the following a message will display that the rule cannot be displayed in visual mode.

**Check functions against Access files and Excel sheets**

Three functions have been added which allow checking the values from the grid against external files:

![](./media/QualityAssuranceModule_image40.png)

-   IsInListAccess checks against another Access file (mdb or accdb).

-   IsInListExcel checks against an Excel file using the Excel column names in the search function.

-   IsInListExcelWithHeaderRow allows the user to define which Excel row contains the header columns and these are used to check the data.

Example:

***IsInListExcel(\[Equipment_ITEMTAG\],
\'C:\\\\data\\\\\_QA\\\\\_Test5\\\\Equipment_Tags.xlsx\', \'test\',
\'A\') == True*** checks if the values from the grid column
"Equipment_ITEMTAG" exist in the Excel Worksheet Sheet name "test",
Column "A".

***IsInListExcel(\[Equipment_ITEMTAG\],
\'C:\\\\data\\\\\_QA\\\\\_Test5\\\\Equipment_Tags.xlsx\', \'test\',
\'A\') == False*** checks if the values from the grid column
"Equipment_ITEMTAG" do not exist in the Excel Worksheet Sheet name
"test", Column "A".

> In the above check functions the name of the file against the check is done can be used with the full path and filename as in the example above but it is also possible to define only the filename in which case the path will be read from the Lookup folder path in application settings.

### Apply quality assurance rules

There are multiple possibilities how rules can be applied: Apply Filter and/or Style, apply multiple filters with / without styles, combine the filters with Or / And etc.

Click the checkbox below "F" to activate the filters, and the checkbox below "S" to activate the styles.

![](./media/QualityAssuranceModule_image41.png)

> To apply all rules at the same time, click on the F or S checkbox of the folder which contains the rules.

### Filter mode

Filter mode defines how quality assurance filters are joined which each other in case more than one filter is enabled in filter manger. The default filter mode is "Or".

To change the filter mode please click on **AND** or **OR**.

### Rename quality assurance rule

To rename a filter please click on the rule name. The cursor will start to blink and you will be able to rename the rule.

### Modify (edit) quality assurance rule

To modify a rule please click on **Edit** in menu bar or right click in filter tree on the ruleand click **Edit** or click on the
![](./media/QualityAssuranceModule_image42.png) icon near rule and the edit rule panel will open.

### Delete quality assurance rule

To delete a rule please click on **Remove** in menu bar or right click in rule tree on the rule and click **Remove**.

### Refresh quality assurance rules

![](./media/QualityAssuranceModule_image43.png)

The Refresh rules command will read the rules from the file system and refresh them within the QualityAssuranceModule; if rules are copied and modified on the filesystem this command can be used to have access to the modified/added rules without closing and reopening the application.

### Import quality assurance rules

![](./media/QualityAssuranceModule_image44.png)

The Import rules command will import a set of rules from an Excel sheet. The sheet must be created with the **Export Rules to Access** command (or have the same format as the one created with this command).

This allows the user to store different rule configurations and to transport them from one project to another or from one environment to another.

The content of the sheet is displayed below (sheet name must be "Rules"):

<img src="./media/QualityAssuranceModule_image45.png" width="736" height="62">

## Working with folders

To group multiple filters, create a folder and move (See Reorder folder) the filters inside it.

### Add folder

To add a folder click on **Add folder** in the menu bar or Right click in the filter tree and click **Add folder**.

### Reorder folder

To add a filter to a folder you can use drag and drop or select the filter and click **Cut** in the menu bar or right click in filter tree on the filter and click **Cut**. Select the destination folder and click **Paste** or right click the destination folder and click **Paste**. If you want to duplicate the filter use **Copy**.

### Delete folder

To delete a folder please select the folder you want to delete and click **Remove** in menu bar or right click in filter tree on the folder and click **Remove**.

> When a folder is deleted which contains filters all filters are also removed (or all nested folders and filters).

## Working with application settings

The application settings menu allows defining the folders for:

-   lookup tables (mdb files)

-   path for shared rules

-   path for shared views

-   path for My Views

![](./media/QualityAssuranceModule_image46.png)

## Activate last project on start

In addition it is possible to check the "Activate last project on start" checkbox. In this case the project which was active when the user closed the application will be activated the next time the user starts the application including all the views which were open.

## Highlighting

Allows choosing the highlight colour and thickness factor. These will be applied when data is highlighted with SmartSketch and when it is saved as PDF.

## Reports

## Reports without template

### Export grid to Excel

<img src="./media/QualityAssuranceModule_image47.png" width="697" height="262">

![](./media/QualityAssuranceModule_image48.png)

This creates an Excel report of the grid including all settings (active
filter, sorting, style, column size, ...).

![](./media/QualityAssuranceModule_image49.png)
The example above shows the content of the grid exported to Excel.

![](./media/QualityAssuranceModule_image50.png)
The sheet "Grid" contains the exported
data.

The sheet "Style legend" contains the list of applied rules including the rule conditions.

### Export rules to Excel

<img src="./media/QualityAssuranceModule_image51.png" width="707" height="258">

## Reports with template

If the user wants to create reports with additional formatting, logos, grouping of data etc. he must use the option "Report with template" and create report templates which will have additional formatting.

There are 3 possibilities for creating report templates:

-   **Standard:** all data will be placed on one sheet

-   **Sheet per group:** each new value of the "GroupField" will
    generate a new sheet in the report

-   **Subgroups:** for every new value of the "SubGroupField" the data will be listed as for the standard report (on the same sheet); in combination with the "GroupField" additional sheets can be generated

![](./media/QualityAssuranceModule_image52.png)

### Example for creating a "Standard" report (from the view "Piping View 1")

![](./media/QualityAssuranceModule_image53.png)

Using the "New" button for Create Report choose a folder for the template and give it a name.

![](./media/QualityAssuranceModule_image54.png)

Then use "Edit" to edit the report and define the fields. The fields can be defined manually (one by one) but there is also an option to place all the fields which are visible in the view on the report (using Add-Ins / Select Column and then choosing the \<ViewName\> / Add all columns function):

![](./media/QualityAssuranceModule_image55.png)

The data will be placed by default on the first Excel row.

In the example below a new line was inserted, the logo was inserted using the Insert Picture function in Excel and the Header information was renamed.

<img src="./media/QualityAssuranceModule_image56.png" width="710" height="201">

The result when using the template:

![](./media/QualityAssuranceModule_image57.png)

The data is displayed on a sheet having the same name as the View.

<img src="./media/QualityAssuranceModule_image58.png" width="674" height="195">

The rules which have been applied are displayed on a sheet called "Style Legend".

> Only if rules are applied to the grid they will be visible on the Excel report.

> If the data fields in the report template were placed using a view file then it is mandatory to create the report with the open and active view as starting point.

### Example for creating a "Sheet per Group" report

This example will use the "PlantGroup Name" as field for grouping the data and will display the data related to the different plantgroup names on different sheets.

![](./media/QualityAssuranceModule_image59.png)

Choose the report type and select the row which will contain the group field.

![](./media/QualityAssuranceModule_image60.png)

Select the position for the group field and choose it using Add-Ins /Select Column

![](./media/QualityAssuranceModule_image61.png)

Place the other fields including the header row in the next rows.

![](./media/QualityAssuranceModule_image62.png)

The result will place each distinct PlantGroup Name with the related data on a new sheet:

![](./media/QualityAssuranceModule_image63.png)

![](./media/QualityAssuranceModule_image64.png)

![](./media/QualityAssuranceModule_image65.png)

The last sheet is the style legend sheet:

<img src="./media/QualityAssuranceModule_image66.png" width="671" height="62">

> Only if rules are applied to the grid they will be visible on the Excel report.

### Example for creating a "Subgroups" report

This example will use the "PlantGroup Name" as field for subgrouping the data and will display the data related to the different plantgroup names grouped by the distinct plantgroup names on the same sheet.

![](./media/QualityAssuranceModule_image67.png)

The field which is used for grouping is placed in the first row.

![](./media/QualityAssuranceModule_image68.png)

![](./media/QualityAssuranceModule_image69.png)

The other fields need to be placed below including the header row.

![](./media/QualityAssuranceModule_image70.png)

The result: all the data is displayed on the same sheet and is grouped by the plant group name.

![](./media/QualityAssuranceModule_image71.png)

![](./media/QualityAssuranceModule_image72.png)

It is also possible to use a combination of Grouping and Subgrouping if the parameter "Group Header Rows" is also used. In this case new sheets will be created as explained in the example "Sheet per Group".

![](./media/QualityAssuranceModule_image73.png)

The parameter "Subgroup footer rows" allows creating a sum of values for instance sum of gate valves per unit or sum of pipe length per unit.

> The Sum function only works for numerical values.

General notes concerning view files and reports:

> 
- 1: If "View" specific columns are used in the reports then the report templates are related to the views and the existence of the view file(s) is mandatory in order to generate the reports.
-  2: It is not mandatory that the views are open when the reports are extracted. If they are not open they will be used in background for the report extraction.
- 3: It is not recommended to rename views if the columns are used in reports. If the view is renamed then the columns must be renamed manually in the report template.
- 4: It is possible to combine data from different views on one report template, for instance Equipment, PipeRun and Instrument data. In this the data for each table will be placed on a separat sheet in the report template.

## Graphical functions (for PID only)

If SmartSketch is installed then the following functions are available in the data grid:

![](./media/QualityAssuranceModule_image74.png)

## Highlight data in drawing

The function "Highlight n objects in drawings" will open SmartSketch and show the marked object(s) in the Highlight Colour and LineWidth Factor.

![](./media/QualityAssuranceModule_image75.png)

If there is more than one drawing involved they will all open and display the selected objects in the corresponding Highlight Colour and LineWidth Factor.

## Export highlighted objects to PDF

This function will convert the drawings to PDFs (including the Highlight Colour and WidthFactor for the selected objects).

<img src="./media/QualityAssuranceModule_image76.png" width="724" height="486"> 

## Export objects with comments to PDF

This function will place comments on the PDF files around the objects
where problems were identified by the rules which are applied to the
grid.

![](./media/QualityAssuranceModule_image77.png)

The comments contain:

ObjectType and ItemTag in the first row EQP:F_234A\ Rule name in the second row EQP Name Null

# Tools

![](./media/QualityAssuranceModule_image78.png)

## Language

The glossary can be selected. If "Default" is active then the database column names will be displayed, if (in case of SPPID) "PIDDisplayName" is selected then the display names defined in the SPPID plant will be displayed. Unless the user chooses the "PIDDisplayName" he will see the default database column names.

## Generate Batch file

If this command is selected the user (admin) will be prompted to select an output file (.xls or .xlsx) and in a second step to select a bat file (text file) which will contain the entries for the command line interface. The parameters are described in Chapter 9.

> The active project settings and filters will be used in the command line mode.

## Log file

![](./media/QualityAssuranceModule_image79.png)
The software creates log files in the local TEMP directory (e.g. "C:\\Temp\\QualityAssuranceModule\\". The user can also open these files by double-clicking on the CAXperts QualityAssuranceModule status bar:

![](./media/QualityAssuranceModule_image80.png)

---

# UniversalReporter — ReportAdapter for Microsoft Excel

# ReportAdapter for Microsoft® Excel®

The **ReportAdapter for Microsoft® Excel®** is used to extract data from an existing **Excel®** file. The resulting **Access** file can be used with **UniversalReporter** or **ApplicationReporter** in order to compare data from **Excel®** sheets with **Access** tables, for example those extracted with other **CAXperts ReportAdapter**s.

## Prerequisites

-   Supported operating systems:
    -   Microsoft® Windows XP
    -   Microsoft® Windows Server 2003
    -   Microsoft® Windows Vista
    -   Microsoft® Windows 7 or later

-   Microsoft® Excel® 2003 or later

## Setting up ReportAdapter for Microsoft® Excel®

### Extracting data

Start **ReportAdapter for Microsoft® Excel®** and select your **Excel®** file by using the **Browse...** button next to the **Excel file** field:

![](./media/ReportAdapterForMicrosoftExcel_image8.png)

> **ReportAdapter for Microsoft® Excel®** always displays the **Excel®** file that was last opened with the application.

Use the **Browse...** button next to the **Access database** field to select the location in which the **Microsoft® Access®** database file is to be created.

Settings are stored automatically in the configuration file shown under **Tools → Settings**, see *Settings*.

Start the process by clicking **Extract** after the attributes to be extracted have been configured, see *Configure attributes*.

If the specified **Access** database already exists, a message box appears asking whether the existing file should be overwritten. If it is overwritten, a backup of the existing file, `.bak`, is created first. Any existing backup file is overwritten with the new backup.

Depending on the settings, see *Add manual extract info*, additional information can be added to the extracted data. If the option **Add manual extract info** is enabled, another window appears before extraction, in which information such as a revision number or description can be entered. This information is stored in the output database and can be recalled in **UniversalReporter** or **ApplicationReporter**.

The **Days valid** field can be used to define the period during which the extracted data remain valid. After this period, **UniversalReporter** or **ApplicationReporter** displays a warning whenever the database is opened.

> **Days valid** has to be a positive number (or empty); all other fields are text fields with up to 255 characters.

![](./media/ReportAdapterForMicrosoftExcel_image10.png)

> To modify or add attributes in the resulting **Access** database, see *Configure attributes*.

## Configure attributes

**ReportAdapter for Microsoft® Excel®** does not contain a predefined set of attributes that are extracted by default. After starting the program for the first time, the user must define and configure the required attributes by using **Tools → Configure attributes**. No database-specific or programming knowledge is required.

![](./media/ReportAdapterForMicrosoftExcel_image11.png)

In the **Configure attributes** window, attributes are configured by Excel sheet. The **Available Excel sheets** combo box contains a list of all available sheets in the selected **Excel®** file. To add a sheet, together with all its attributes, to the **Available attributes** tree, select the **Excel®** sheet from the combo box or enter its name in the **Available Excel sheets** field. Then enter the header row number, that is, the row containing the column names of the selected sheet, in the **Header row number** field and click **Add to available attributes**. The Excel sheet name, together with all its attributes, then appears in the **Available attributes** tree.

From **Available attributes**, the user can access all attributes defined in the **Excel®** header row. Names displayed in brackets are the column names in the resulting **Access** database, see *Attribute mapping*. The right-hand side, **Used attributes**, contains the attributes defined for extraction.

> Column names in Access must not include spaces or special characters. These are therefore removed from all attribute names.

Drag the required attributes from the left-hand side to the right-hand side, or select them and use the **>** button. Drag from right to left, or use the **<** button, to remove attributes from the **Used attributes** list. A maximum of 230 attributes per sheet can be selected as **Used attributes**. If this number is exceeded, the sheet name is shown in red.

The **Search in available attributes** field can be used to find an attribute or its mapped name in the **Available attributes** tree. If the search field contains a search term, the **Available attributes** tree displays only those attributes containing that term. To return to the full tree, remove the search term from the **Search in available attributes** field.

To remove a sheet from **Available attributes**, select the sheet name from the **Available Excel sheets** combo box, or enter the name in the **Available Excel sheets** field, and use the **Remove from attributes** button. All **Used attributes** belonging to that sheet are removed as well.

Press **OK** to finish the configuration. The changes are applied during the next extraction.

> The attribute configuration is saved and reloaded whenever the same **Excel®** file is used.

## Attribute mapping

When comparing **Excel®** data in **UniversalReporter** with data from other applications, a mapping of attribute names may be necessary. It is possible to define a separate sheet in the **Excel®** file containing this mapping information, see *Settings*. If no mapping is available, the attribute names from the **Excel®** headers are used as the column names of the **Access** tables and all attributes are extracted as text. When mapping is used, the mapped names are displayed in brackets in the **Configure attributes** window. The mapping sheet must be defined as shown in the following example:

![](./media/ReportAdapterForMicrosoftExcel_image12.png)

The mapping range to be specified in **Settings** is the range marked in red, in the example `A4:E15`. The header of this range is ignored. Only the sequence of columns is relevant.

The first column must contain the header name used in the **Excel®** sheets, written exactly as it appears in the sheet. The second column is the name to be used as the column name in the **Access** database. The third column is needed only if data from **UniversalReporter** are to be written back to **Excel®**, see *Manual_UniversalReporter for SmartPlant*. In this column, **Read only** means that the value in the **Excel®** sheet is never changed, while **allow insert** means that the values of existing rows are not changed, but the value is inserted for new rows. In general, all **Excel®** fields are exported as text. If required, however, a number format can be defined in the fourth column of the mapping sheet. The optional data column defines attribute behaviour in **UniversalReporter**, see *Manual_UniversalReporter for SmartPlant / Rule data window*.

## Additional SQL statements 

### Create additional SQL statements

SQL statements used to extend standard output tables with user-defined attributes, to combine output tables, or to perform other tasks, such as creating additional tables or queries, can be defined by using **Tools → Additional SQL statements**. These statements are executed automatically after the extraction process has been completed.

The additional SQL statements can be entered in the **Additional SQL statements to be processed after extraction** field. They should follow the format shown in the following examples:

SQL1=ALTER TABLE Equipment ADD COLUMN \[MyAttribute\] VARCHAR(255)

SQL2=UPDATE \[Equipment\] SET \[MyAttribute\] = \'Test\'

![](./media/ReportAdapterForMicrosoftExcel_image13.png)

The tables that can be used in the queries are those that are created during extraction. Additional tables, if required, must be copied before extraction into the `Template.mdb` database, which can be found in the application folder.

### Use query builder

The included query builder can assist in creating SQL statements. Create a new statement with the **Add** button and add one of the **Available SQL query templates** to it. Clicking **Add query**, or double-clicking a list item, inserts the query at the cursor position or replaces the selected text.

The **Available SQL query templates** look like:

UPDATE \[%TABLE1%\] SET \[%TABLE1%\].\[%COLUMN1%\]=

When the template is used, the `%TABLE1%` and `%COLUMN1%` fields are filled with the table name and column name selected in the corresponding fields.

> The `%TABLE1%` and `%COLUMN1%` fields are filled from the Access database defined for extraction output, which also includes the tables from `Template.mdb`. This means that the fields are populated only after extraction has been performed once.

If the **Show hidden tables** field is checked, all tables in the database can be selected in order to build the query. If it is not checked, only the tables displayed in **UniversalReporter** are available, while tables created for internal use remain hidden.

### Create new SQL query template

User-defined **SQL query templates** can be created. To add a new template, click **Add** and enter the template name.

![](./media/ReportAdapterForMicrosoftExcel_image14.png)

Enter the SQL statement text and the placeholders for the **Tables and columns to be used by query** text boxes by using the `%TABLE1%` and `%COLUMN1%` buttons.

User-defined templates can be deleted or modified by using the **Remove** or **Edit** buttons.

> The SQL text boxes support syntax highlighting.

## Settings

All program settings are stored automatically for each **Excel®** file in the **ReportAdapter** configuration file, which is located by default in the user profile folder. The configuration file can be changed by using **Tools → Settings**.

### Load settings from file

To import settings into the current configuration file, use **File → Load settings from file**.

> The location of the configuration file is not affected, only the content of the file will be overwritten.

### Save settings to file

To export the settings from the current configuration file, use **File → Save settings to file**.

> The location of the configuration file is not affected.

### Add manual extract info

On the **General** tab under **Tools → Settings**, the user can configure whether additional information should be entered and stored together with the extracted data.

Additional information can be entered before extraction only if **Add manual extract info** is checked.

If **Keep last values** is checked, the **Information** window is filled with the values most recently used for the selected plant.

![](./media/ReportAdapterForMicrosoftExcel_image15.png)

### Mapping

For attribute mapping, the name of the mapping sheet and the range containing the mapping data, see *Attribute Mapping*, must be defined on the **Mapping** tab:

![](./media/ReportAdapterForMicrosoftExcel_image16.png)

## Content of result database

When the extraction process has finished, the resulting database in **Microsoft® Access** is available in the specified location. The database contains the configured tables and attributes. Some key attributes, for example `OID`, are extracted regardless of whether they were configured explicitly. These are required for internal use.

---

# UniversalReporter — ReportAdapter for SmartPlant 3D

# ReportAdapter for Smart® 3D

**ReportAdapter for Smart® 3D** is designed to export **Smart 3D** attributes and graphics into a SQLite database containing one table per task. The resulting records are compressed and internally resolved by code lists. All data can be extracted without any knowledge of SP3D-specific report creation. The intended approach is to use **Smart 3D** workspace filters for the creation of discipline-specific reports.

## Prerequisites

-   Supported operating systems:
    -   Microsoft Windows Server 2008, 2012
    -   Microsoft Windows 7 or later

-   Intergraph Smart 3D 2009 SP5 or later

> ReportAdapter for Smart® 3D requires Smart 3D and a Smart 3D (S3D) licence seat on a Smart Licence Server.

## Setting up ReportAdapter for Smart® 3D

### Select plant

Start **ReportAdapter for Smart® 3D** and select your model plant from the **Plant** combo box:

![](./media/ReportAdapterForSmartPlant3D_image5.png)

> Only plants of the current site are displayed.

### Select filter

To select a filter, click the **Filter** combo box and select **More filters...**

![](./media/ReportAdapterForSmartPlant3D_image6.png)

A window opens listing all available filters. Select the required filter and click **OK**.

![](./media/ReportAdapterForSmartPlant3D_image7.png)

### Select output

Use the **Browse...** button to select the location in which the Microsoft Access (`*.mdb`) or SQLite database (`*.db`) is to be created.

![](./media/ReportAdapterForSmartPlant3D_image8.png)

All settings are stored automatically in the configuration file shown under **Tools → Settings**.

## Configure attributes

**ReportAdapter for Smart® 3D** contains a predefined set of attributes that are reported by default. In addition, users can define and configure their own attributes. No database-specific or programming knowledge is required in order to customise the reportable **Smart 3D** attributes. A table is a combination of attribute sets, and columns with identical names are aligned vertically.

![](./media/ReportAdapterForSmartPlant3D_image9.png)

![](./media/ReportAdapterForSmartPlant3D_image10.png)

### Add new table

To add a new table, right-click in the right-hand panel and click **New table**.

### Attribute set

Each table consists of one or more attribute sets. Each attribute set contains the data for a specific top node. The result of one or more attribute sets is grouped together in the table to which they belong in the output database.

### Add new attribute set

To add a new attribute set, right-click a table and click **New attribute set**.

### Add new attribute

![](./media/ReportAdapterForSmartPlant3D_image11.png)

Drag and drop an attribute into the table on the right-hand side. The attribute then appears under the relevant attribute set.

### Edit attribute set properties

To define the properties of a table, right-click the attribute set and select **Properties**.

![](./media/ReportAdapterForSmartPlant3D_image12.png)

![](./media/ReportAdapterForSmartPlant3D_image13.png)

The type selected here defines the main item used for reporting. For example, with `pipe component → run`, if a pipe component is selected, only the runs belonging to that component are listed. If `run` is selected, empty runs are also listed.

Press **OK** to finish the configuration.

<table>
  <tbody>
    <tr>
      <td>The changes will be applied on the next extraction.</td>
      <td>Smart 3D labels can be used in the same way as direct object attributes.</td>
    </tr>
  </tbody>
</table>

### Extended attribute settings

Right-click a code-listed attribute to open the extended attribute configuration form:

![D:\\01_Projekte\\98_capture\\2014-05-26_174450_cr.jpg](./media/ReportAdapterForSmartPlant3D_image14.jpg)

Code-listed attributes can be extracted as:

-   Numeric values

-   Values decoded by short description

-   Values decoded by long description (default)

![](./media/ReportAdapterForSmartPlant3D_image15.png)

For values with units, the units can be selected:

![](./media/ReportAdapterForSmartPlant3D_image16.png)

<table>
  <tbody>
    <tr>
      <td></td>
      <td><p>The changes will be applied on the next extraction.</p>
      <p>By default, all attributes use the common unit configuration as
      defined in <strong>Menu -&gt; Unit settings</strong>. Here you can select a
      custom unit for a specific attribute.</p></td>
    </tr>
  </tbody>
</table>

#### Find

Select **Find...** from the **Edit** menu to search for available attributes:

![](./media/ReportAdapterForSmartPlant3D_image17.png)

Enter the search text in **Find what** and define the task name in **Look in** in order to search for the attribute in the selected task:

<img src="./media/ReportAdapterForSmartPlant3D_image18.png" width="308" height="244">

## Geometry extraction

**ReportAdapter for Smart® 3D** allows geometry to be extracted from **Smart 3D**. This geometry is used by CAXperts **UDiTH** and CAXperts **S3D2PDS**.

Enable the **Extract Geometry** option and select an existing 3D DGN drawing.

![](./media/ReportAdapterForSmartPlant3D_image19.png)

### Prerequisites

1.  Bentley MicroStation J (V7) has to be installed on the Workstation.

2.  Empty 3D DGN drawing has to be created through the Smart 3D Drawing
    Console, e.g. like this:\
    ![](./media/ReportAdapterForSmartPlant3D_image20.png)\
    This 3D DGN drawing is used by **ReportAdapter for Smart® 3D** for geometry extraction. The user needs permission to update this drawing.

3.  Permission to create new filter in the folder ("Plant filters\\3D
    ReportAdapter" by default).

4.  Write permissions on Shared Content (Drawings\\Catalog\\Rules) to
    be able to create new view or style rules.

<table>
  <tbody>
    <tr>
      <td></td>
      <td><p>Technical Hint regarding MicroStation limitation:</p>
      <p>Geometry extraction by using ReportAdapter for Smart® 3D is
      based on Intergraph Smart 3D export to a 3D DGN file and has
      the same limitations: only graphical model data in a range of
      -26843 m to 26843 m in the XYZ direction, for the M/MM DGN
      seed file, and from -88069 &#39; to 88069 &#39; in the XYZ direction,
      for the &#39;/&#39; DGN seed file, are exported into MicroStation
      files. Model data outside this range are not exported.</p></td>
    </tr>
  </tbody>
</table>

## Extraction

To start extraction of attributes, and geometry if enabled, click **Extract**.

## Additional SQL statements 

### Create additional SQL statements

SQL statements can be used to extend standard output tables with user-defined attributes or to perform other tasks, such as creating additional tables or queries. These statements can be defined by using **Tools → Additional SQL statements**. They are executed automatically after the extraction process has been completed.

The additional SQL statements can be entered in the **Additional SQL statements to be processed after extraction** field. They should follow the format shown in the following examples:

SQL1=ALTER TABLE Equipment ADD COLUMN \[MyAttribute\] VARCHAR(255)

SQL2=UPDATE \[Equipment\] SET \[MyAttribute\] = \'Test\'

![](./media/ReportAdapterForSmartPlant3D_image21.png)

The tables that can be used in the queries are those created during extraction. Additional tables, if required, must be copied before extraction into the `Template.mdb` database, which can be found in the application folder.

### Use query builder

The included query builder can help to create SQL statements. Create a new statement with the **Add** button and add one of the **Available SQL query templates** to it. Clicking **Add query**, or double-clicking a list item, inserts the query at the cursor position or replaces the selected text.

The **Available SQL query templates** look like:

UPDATE \[%TABLE1%\] SET \[%TABLE1%\].\[%COLUMN1%\]=

When the template is used, the `%TABLE1%` and `%COLUMN1%` fields are filled with the table name and column name selected in the corresponding fields.

> The `%TABLE1%` and `%COLUMN1%` fields are filled from the Access database defined for extraction output, which also includes the tables from `Template.mdb`. This means that the fields are filled only after extraction has been performed once.

If the **Show hidden tables** field is checked, all tables in the database can be selected when building the query. If it is not checked, only the tables displayed in **UniversalReporter** are available, while tables created for internal use remain hidden.

### Create new SQL query template

User-defined **SQL query templates** can be created. To add a new template, click **Add** and enter the template name.

![](./media/ReportAdapterForSmartPlant3D_image22.png)

Fill in the text of SQL statement and the Placeholders for **Tables and
columns to be used by query** textboxes by using the %TABLE\"1% and
%COLUMN1% buttons.

User defined templates can be deleted or modified using the **Remove**
or **Edit** buttons.

<table>
  <tbody>
    <tr>
      <td></td>
      <td><p>The SQL text boxes support syntax highlighting.</p>
      <p>Depending on the output database format, Microsoft Access
      (<code>*.mdb</code>) or SQLite (<code>*.db</code>), the SQL syntax may differ.</p></td>
    </tr>
  </tbody>
</table>

## Settings

All program settings, such as selected tasks and filters, attributes, and units of measure, are stored automatically for each plant in the configuration file, which is located by default in the user profile folder. The location of the configuration file can be changed by using **Tools → Settings**.

### General

![](./media/ReportAdapterForSmartPlant3D_image23.jpg)

**Add manual extract info**: if enabled, shows the following dialogue before extraction, in which the user can provide additional information evaluated by CAXperts **UniversalReporter**.

![](./media/ReportAdapterForSmartPlant3D_image24.png)

**Clean-up style rules and filter before extraction, do not use for parallel extractions:** if geometry extraction is enabled, this option removes all temporary style rules and filters that may have been left from the previous run of the software. Normally, this option must not be used when more than one extraction of **ReportAdapter for Smart® 3D** is being run on the same project at the same time.

**Extract interferences:** if geometry extraction is enabled, this option extracts interference bubbles, graphics, together with their attributes.

**Custom coordinate system:** to report coordinates by using a custom coordinate system, `CS`, instead of the global coordinate system, the default, click **More** and select the required `CS`.

![](./media/ReportAdapterForSmartPlant3D_image25.png)

> The custom coordinate system is applied only to attribute extraction, that is, reporting, and has no effect on extracted geometry.

**Force Exit extraction job:** provides an additional way to ensure that an extraction job is completed by **3D ReportAdapter**. This can be helpful especially when several extraction jobs are scheduled one after another. This option is also available as a command-line parameter. **Force Exit extraction job** uses a `lite` disconnect from S3D. Use this option only if there is a problem with the standard exit from **3D ReportAdapter**.

### Surface style rules

The colour is defined in a standard S3D Surface Rule Style that is preconfigured in the S3D Surface Rule Style settings. Here you can define the MicroStation levels for the different aspects of a particular object type.

Select the rules to apply from the list of available Surface Rule Styles on the left-hand side and press **Add**.

![](./media/ReportAdapterForSmartPlant3D_image26.png)

The rules are executed from top to bottom. If no style rules are defined, standard colours and MicroStation levels are used.

### Drawings

Defines the isometric and composed drawings to be extracted. To exclude drawings with a specific approval status, clear the corresponding **Approval Status** in the list.

![](./media/ReportAdapterForSmartPlant3D_image27.png)

> Only drawings checked in tree with selected approval status will be extracted

### Piping Catalogue

Defines the piping specifications to be extracted.

![](./media/ReportAdapterForSmartPlant3D_image28.png)

### Check objects in filter

3D ReportAdapter can check whether an extracted object is included in selected filters.

![](./media/ReportAdapterForSmartPlant3D_image29.png)

If at least one filter is selected, the software creates an `ObjectsInFilter` table in the resulting database, with the two columns `Object OID` and `Filter Name`. This functionality is useful, for example, when reporting volumes that include the object.

### Load settings from file

To import settings into the plant configuration file, use **File → Load settings from file**.

> The location of the plant configuration file is not affected. Only the content of the file is overwritten.

### Save settings to file

To export settings from the plant configuration file, use **File → Save settings to file**.

> The location of the plant configuration file is not affected.

## Command line mode

The **ReportAdapter for Smart® 3D** can be used from the command line to automate the export of **Smart 3D** data.

To open the **ReportAdapter** batch-mode help, enter the installation path of **ReportAdapter** followed by `\3D ReportAdapter.exe /?`.
```
"C:\Program Files\CAXperts\3D ReportAdapter\3D ReportAdapter.exe" /?
```
Full syntax:
```
"3D ReportAdapter.exe" [?] [-plant:ExamplePlant] [-config:c:\config_file.ini] [-filter:My Filters\Filter1] [-output:output_file.mdb] [-cleanrules] [-drawing:Plant\New 3D DGN Drawings\Drawing-0001] [-extractgeometry] [-permissiongroup: Admin] [-forceattributes] [-forcegeometry] [-forceexit:1] [-log:D:\temp]
```

<table>
  <tbody>
    <tr>
      <td>-plant plant_name</td>
      <td>Smart 3D plant to process</td>
    </tr>
    <tr>
      <td>-config config_file.ini</td>
      <td>Configuration file containing the extraction settings. If no configuration file is defined, the last configuration is used</td>
    </tr>
    <tr>
      <td>-filter filter_name</td>
      <td>Filter name from the S3D plant. This can be a plant filter or a filter from My Filters. If no filter is defined, the last configuration is used</td>
    </tr>
    <tr>
      <td>-output output_file.mdb</td>
      <td>Output database. If no output database is defined, the last configuration is used</td>
    </tr>
    <tr>
      <td>-cleanrules</td>
      <td>Cleans leftover temporary S3D2PDS-specific rules. This is optional</td>
    </tr>
    <tr>
      <td>-drawing</td>
      <td>Path to the 3D DGN drawing from the S3D Drawing Console</td>
    </tr>
    <tr>
      <td>-extractgeometry</td>
      <td>If this switch is set, S3D2PDS generates a PD_XPDA backup file</td>
    </tr>
    <tr>
      <td>-permissiongroup</td>
      <td>Permission group used for extraction</td>
    </tr>
    <tr>
      <td>-forceattributes</td>
      <td>If not defined, only changed attributes are extracted</td>
    </tr>
    <tr>
      <td>-forcegeometry</td>
      <td>If not defined, only changed geometry is extracted</td>
    </tr>
    <tr>
      <td>-forceexit</td>
      <td>Forces the extraction job to exit by using a <code>lite</code> disconnect from S3D. Use this option only if there is a problem with the standard exit from 3D ReportAdapter</td>
    </tr>
    <tr>
      <td>-log</td>
      <td>If not defined, the log file is written to the local Temp directory</td>
    </tr>
  </tbody>
</table>

---

# UniversalReporter — ReportAdapter for SmartPlant PID

# ReportAdapter for SmartPlant® P&ID

**ReportAdapter for SmartPlant® P&ID** is designed to extract data from Intergraph SmartPlant® P&ID (SPPID) into a local project database in Microsoft Access format.

## Installation

Supported operating systems:

-   Microsoft Windows XP
-   Microsoft Windows Server 2003
-   Microsoft Windows Vista
-   Microsoft Windows 7 or later
-   Intergraph SmartPlant P&ID 4.3 or later

## Graphical User Interface (GUI)

### Invoking ReportAdapter for SmartPlant® P&ID

You can launch the utility by clicking **ReportAdapter for SmartPlant® P&ID** from the programme menu, as shown below:

![](./media/ReportAdapterForSmartPlantPID_image9.png)

The following screen will appear:

![](./media/ReportAdapterForSmartPlantPID_image10.png)

### Loading a Plant

The software automatically connects to the active plant. This is displayed in the **Plant** field and can be changed using the **Change active plant...** button.

From the **Open plant structure** dialogue, you can select another plant at the active site. To access plants at other sites, use the **Site server...** button.

![](./media/ReportAdapterForSmartPlantPID_image11.png)

Before loading the plant data, specify the **Output Access file** using the **Browse...** button.

### Extracting Data

Click the **Extract** button to begin extraction from the selected plant. Various extraction options are available: **All**, **Selected drawings**, **Associated data**, and **Extract drawing files**.

#### General Extraction Settings

##### All

The **All** option extracts all plant data without requiring further configuration.

##### Selected Drawings

The **Selected drawings** option opens a dialogue where you can select the drawings you require.

A list of available drawings from the active project will appear. Tick the desired drawings and click **OK** to begin the extraction.

![](./media/ReportAdapterForSmartPlantPID_image12.png)
![](./media/ReportAdapterForSmartPlantPID_image13.png)

In Connected Workshare projects, drawings that belong to satellite configurations are displayed in red to inform you of their status.

##### Associated Data

The **Associated data** option opens a dialogue where you can define the data you wish to extract using SQL statements.

![](./media/ReportAdapterForSmartPlantPID_image14.png)

For example, the statement above will extract only pipe runs whose tags begin with "05". Similarly, you can extract pipe runs with a specific FluidSystem (such as "Water") or Nominal Diameter (such as "100 mm").

You can also use this interface to search for data—for example, to locate a specific Sequence Number used in the plant. Simply enter your search term in the yellow bar without performing an extraction:

![](./media/ReportAdapterForSmartPlantPID_image15.png)

##### Revision

The **Revision** option allows you to extract frozen data (only available for revisions with an associated version). The dialogue lets you choose between "Last Revision" data or data from a specific revision:

![](./media/ReportAdapterForSmartPlantPID_image16.png)

##### Extract Drawing Files

The **Extract drawing files** option copies drawing files to a folder named "igr" at the same location as the mdb file and renames them with the ".igr" extension.

>  Please ensure the SmartSketch version is compatible with your version of SmartPlant P&ID; otherwise, the highlight function in **UniversalReporter** will not work.

#### Settings Related to the from/to Calculation

The following options can be used when extracting the from/to
information:

![](./media/ReportAdapterForSmartPlantPID_image17.png)

- **Only equipment tag:** Reports only equipment tags (no nozzle tags)

- **No branches:** Does not report branch connections

- **Use component description if no tag:** Reports the Piping Component Description attribute if the component tag is empty and the component is an "EndComponent"

In addition to these settings the following changes were implemented:

##### TEMA Heat Exchangers

![](./media/ReportAdapterForSmartPlantPID_image18.png)
If the front and rear sections do not have an item tag defined, the shell item tag is also extracted as From/To information for pipe runs connected to those sections.

In the example above, all runs connected to parts of the heat exchanger will have "W-170" as the from or to information.

##### Equipment with no Tag

For equipment with no tag, you can use a variable to specify which attribute should be used as from/to information (such as "Name" or "Description").

To do this, edit the PID ReportAdapter ini file:

```
..\Program Files(x86)\CAXperts\PID ReportAdapter\PID ReportAdapter.ini
```

```
[FROM_TO]
Equipment_NoTag=Name
```

#### Keep Original Tables During Extraction

If you wish to retain the original tables created during extraction, you can configure a setting in the **PID ReportAdapter** ini file to prevent their deletion:

```
[Settings]
Pid_Delete_TempTables=0
```

If you add this parameter and set it to 0, the tables will not be deleted, and tables such as t_equipment and t_piperun will remain available in the mdb file.

If you set it to 1, the tables will be deleted. This is the default behaviour if you do not include the parameter in the ini file.

#### Attributes on Drawing Objects

A setting has been implemented that activates a checkbox for drawing extraction:

![](./media/ReportAdapterForSmartPlantPID_image19.png)

```
[Settings]
DrawingAttributes=1
```

If you add this parameter and set it to 1, the checkbox "Add attributes" becomes available in the **PID ReportAdapter** interface.

If you tick this box before extraction, PID properties will be copied to the elements in the SmartSketch file (visible in the SmartSketch properties window).

![](./media/ReportAdapterForSmartPlantPID_image20.png)

>This operation may take several minutes per drawing. For large datasets, we recommend running it in batch mode.

### Additional SQL Statements 

#### Create Additional SQL Statements

You can define SQL statements using **Tools → Additional SQL statements** to extend standard output tables with user-defined attributes or perform other tasks (such as creating additional tables or queries). These will be executed automatically after the extraction is complete.

Enter your additional SQL statements in the **Additional SQL statements to be processed after extraction** field. Please use the following examples as a guide:

```
SQL1=ALTER TABLE Equipment ADD COLUMN [MyAttribute] VARCHAR(255)
SQL2=UPDATE [Equipment] SET [MyAttribute] = 'Test'
```

![](./media/ReportAdapterForSmartPlantPID_image21.png)

You can use any tables that will be created during extraction. If you need additional tables, copy them to the template.mdb database (located in the application folder) before running the extraction.

#### Use Query Builder

The included query builder can help you create SQL statements. To add a new statement, click the **Add** button, then select one of the **Available SQL query templates**. Clicking **Add query** or double-clicking a template will insert it at the cursor position or replace your selected text.

The **Available SQL query templates** look like:

```
UPDATE [%TABLE1%] SET [%TABLE1%].[%COLUMN1%]=
```

When you use a template, the %TABLE1% and %COLUMN1% placeholders will be populated with the table and column names you select.

>The ```%TABLE1%``` and ```%COLUMN1%``` fields are populated from the Access database defined for extraction output, which includes tables from Template.mdb. These fields are only available after you have performed the extraction at least once.

If you tick the **Show hidden tables** option, you can select all database tables for your query. If left unticked, only tables displayed in **UniversalReporter** are available; internally created tables remain hidden.

#### Create New SQL Query Template

User defined **SQL query templates** can be created. To add a new
template, click the **Add** button and enter the template name.

![](./media/ReportAdapterForSmartPlantPID_image23.png)

Fill in the text of SQL statement and the placeholders for **Tables and columns to be used by query** textboxes by using the ```%TABLE1%``` and ```%COLUMN1%``` buttons.

User defined templates can be deleted or modified using the **Remove** or **Edit** buttons.

>The SQL text boxes support syntax highlighting for improved readability.

### Add Manual Extract Info

Under **Tools - Settings**, you can configure whether to enter additional information to store with the extracted data. Additional information can only be added before extraction if **Add manual extract info** is ticked.

If **Keep last values** is ticked, the **Information** window will be populated with the values previously used for the selected plant.If **Keep last values** is ticked, the **Information** window will be populated with the values previously used for the selected plant.

![](./media/ReportAdapterForSmartPlantPID_image24.png)

If **Add manual extract info** is enabled, an additional window will appear before extraction, where you can enter information such as revision numbers or descriptions. This information is stored in the output database and can be retrieved in **UniversalReporter** or **ApplicationReporter**.

Use the **Days valid** field to specify how long the extracted data will remain valid. After this period, **UniversalReporter** or **ApplicationReporter** will display a warning when the database is opened.

>**Days valid** must be a positive number or left empty. All other fields are text fields with a maximum of 255 characters.

![](./media/ReportAdapterForSmartPlantPID_image25.png)

### Configure Attributes

#### General Functions

Select **Tools → Attribute configuration...** to open the **Attribute configuration** dialogue.

![](./media/ReportAdapterForSmartPlantPID_image26.png)

The following window will appear.

![](./media/ReportAdapterForSmartPlantPID_image27.png)

This dialogue allows you to define which attributes will be extracted.

Currently, you can add attributes for six object types: Inlinecomp_prun, Eqp_Nozzle, Plantitems, Instruments, ItemNotes, OPCs, and Drawings.

Additional attributes (which are not available in SmartPlant P&ID but are created during extraction) appear in blue. For example, "End_2_ConnectedPiperunID" in the screenshot above.

The numbers shown on the right side of the tables indicate the total number of available attributes (left panel) and the number of selected attributes (right panel).

Expand the tables and select the required attributes. You can transfer attributes between panels using the > and < buttons.

The attribute configuration is saved in the folder %APPDATA%\CAXperts\PID ReportAdapter\ with the filename PlantName.ini. You can also save the attribute configuration for backup purposes or for use in batch mode extraction:

![](./media/ReportAdapterForSmartPlantPID_image28.png)

Attribute configuration specialties:

#### ParentPlantGroup and UnitCode

For SPEM Hierarchies such as Plant\Area\Unit, you can extract Parent PlantGroup information and UnitCode information:

![](./media/ReportAdapterForSmartPlantPID_image29.png)

This allows you to filter easily by Area, for example. This option is available for all objects except ItemNotes and OPCs.

#### PipingPoint Attributes Including ConnectedPiperunID

For piping connection points 1 to 4, you can extract attributes such as nominal diameter, description, and the SP_ID of the connected pipe run. This information can be used to create reports that display which pipe run connects to the second side of a pressure-reducing valve, for example.

![](./media/ReportAdapterForSmartPlantPID_image30.png)

The result in **UniversalReporter** will look like this:

![](./media/ReportAdapterForSmartPlantPID_image31.png)

#### CaseProcess SI Values (also other properties SI values)

The SI values can also be selected for extraction.

![](./media/ReportAdapterForSmartPlantPID_image32.png)

#### Filename and Quantity

If you select "Filename" at the "PlantItem" object level, the symbol name will also be extracted, allowing you to see how many times a symbol appears on a drawing.

If you select "Quantity" (available at all object levels), each extracted object will be assigned a quantity of "1". In **UniversalReporter**, you can then create a "Sum_of_quantity" field to identify the total number of items (such as valves).

![](./media/ReportAdapterForSmartPlantPID_image33.png)

#### Connectivity Attributes for the Relations "Equipment to PipeRun" and "Instruments to PipeRuns or Equipments"

Even though not visible in the attribute configuration interface the following two attributes are extracted:

- **Nozzle_ConnectedPipeRunID** for the Eqp_Nozzle table.

- **Instrument_ConnectedItemID** for the Instruments table.

You can use these attributes to create reports that list the pipe runs connected to specific equipment (starting from the equipment object level) or to identify instrument connections (which may be to pipe runs or equipment nozzles).

**Example for the Equipment_PipeRun Relation**:

![](./media/ReportAdapterForSmartPlantPID_image34.png)
![](./media/ReportAdapterForSmartPlantPID_image35.png)

![](./media/ReportAdapterForSmartPlantPID_image36.png)

**Example for the Instrument Connection Report**:

There are multiple ways to connect instruments to nozzles or piperuns.

The following connection types (involving piping components positioned between the connected pipe run or equipment nozzle and the instrument) have been modified in PID ReportAdapter:

- **A**: The instrument connects to a valve placed on a branch of the main pipe run. The branch is returned as the connected object.

- **B**: The instrument connects to a valve that connects to a nozzle (with pipe run type "Conn to process/Supply"). The nozzle is returned as the connected object.

- **C**: The instrument connects to a valve linked to a nozzle via a "ZeroLength" pipe run. The nozzle is returned as the connected object.

- **D**: The instrument connects to a valve placed on a pipe run that connects to a nozzle. The pipe run has a special linetype. The nozzle is returned as the connected object (in the example below, the linetype is "Components only").

You can define the pipe run type in the ```.\PID ReportAdapter.ini``` file using the following setting:

```
[Instr_Conn]
Nozzle_Pruntype = "Components only"
```

![](./media/ReportAdapterForSmartPlantPID_image37.png)
![](./media/ReportAdapterForSmartPlantPID_image38.png)

Details for C and D:

![](./media/ReportAdapterForSmartPlantPID_image39.png)

Result:

![](./media/ReportAdapterForSmartPlantPID_image40.png)

#### Disconnected Piperuns Having the Same ItemTag

If you select "NotConnectedRuns" under the "InlineComp_Prun" object level in "Others", the software will calculate the number of disconnected pipe runs with the same tag in your extracted dataset.

![](./media/ReportAdapterForSmartPlantPID_image41.png)

![](./media/ReportAdapterForSmartPlantPID_image42.png)

The function checks if runs are graphically connected within a drawing. Across drawings, it checks whether pipe runs are connected via OPCs. Any result greater than 0 indicates duplicate pipe run tags that are not graphically connected.

>Please note that extracting disconnected runs may take some time with larger datasets.

#### Identify Number of LineNumberLabels Placed on a Piperun

For all pipe runs except instrument runs and zero-length pipe runs, if you select the "NumberofLabels" attribute and define the label name(s) in the plant-specific ini file, the software will calculate how many labels are placed on each pipe run during extraction.

In the ini file (```.\UserProfile\Appdata\Roaming\CAXperts\PID ReportAdapter\<PlantName>.ini```), add the relative path for the line labels you wish to check:

![](./media/ReportAdapterForSmartPlantPID_image43.png)

To check multiple labels, separate them with a semicolon, as shown in the example below:

```
Piping=\Piping\Labels - Piping Segments\Line Number.sym;\Piping\Labels - Piping Segments\Line Number_old.sym
```

Review any result that is not 1 (0 means no label is placed; >1 means more than one label is placed on the same run).

![](./media/ReportAdapterForSmartPlantPID_image44.png)

#### Identify the "Parent" Relationship Between Components

By using the "SP_PartOfId" attribute (available in the PlantItem area for all components), you can now extract relationships between actuators and valves, equipment components and equipment, and so on.

![](./media/ReportAdapterForSmartPlantPID_image45.png)

![](./media/ReportAdapterForSmartPlantPID_image46.png)

#### Number of Versions and Number of Auxiliary Graphic Files for Drawings

By using the NoOfAuxGraphic and NoOfVersions attributes (available in the drawing area), this information is calculated during extraction and is available as a field in **UniversalReporter/QualityAssuranceModule**.

![](./media/ReportAdapterForSmartPlantPID_image47.png)

#### Drawing Revision Information

You can select attributes from t_revision for extraction. Additionally, you can select "LastRevision" to display the latest revision of a drawing, and "VersionAssociated" to show whether a version is associated with that revision.

![](./media/ReportAdapterForSmartPlantPID_image48.png)

#### SymbolName and Coordinates

The Symbol table has been added to the extraction interface, allowing you to select fields from the symbol table, including the calculated field "Name", which refers to the symbol name (for example, "Gate Valve").

![](./media/ReportAdapterForSmartPlantPID_image49.png)

#### Parameter for all Plant Loops

To extract plant loops that do not have associated instruments, define the following parameter in the plant-specific ini file (```%Appdata%\CAXperts\PID ReportAdapter``` folder):

```
[General]
AllLoops=1
```

#### MultiRep for Equipment

A new attribute called "MultiRep" has been added to the Equipment_Nozzle attribute configuration (in the "Others" area):

![](./media/ReportAdapterForSmartPlantPID_image50.png)

This is calculated during extraction and displays "True" for MultiRepresentation equipment and "False" for other equipment.

#### As-Built Information

A new plant attribute called "Asbuilt-name" has been added to the _Info table, making it available in **UniversalReporter** as a parameter for report template creation. This is calculated during extraction and displays the name of the As-Built plant when data is extracted for projects.

### Command Line Mode

**PID ReportAdapter** can also be used in command line mode to automatically extract all data from a plant to an mdb file via a batch script. Use the following command to view the command line arguments:

![](./media/ReportAdapterForSmartPlantPID_image51.png)

Command line arguments:

<img src="./media/ReportAdapterForSmartPlantPID_image52.png" width="959">

Examples:

```"C:\Program Files\CAXperts\PID ReportAdapter\PID ReportAdapter.exe" /output "c:\temp\PlantA.mdb"```

```"C:\Program Files\CAXperts\PID ReportAdapter\PID ReportAdapter.exe" /output "c:\temp\PlantA.mdb" /site "d:\Site1\smartplantv4.ini" /plant PlantA /parentplant PlantA /config "d:\PlantA.ini"```

```"C:\Program Files\CAXperts\PID ReportAdapter\PID ReportAdapter.exe" /output "c:\temp\PlantA.mdb" /pbs Refining Area\Recovery Unit;\Refining Area\Distillation Unit\a_*``` will save all drawings with names starting with ```a_``` located in the ```Refining Area\Recovery Unit``` or ```Refining Area\Distillation Unit```

```"C:\Program Files\CAXperts\PID ReportAdapter\PID ReportAdapter.exe" /output d:\temp\1.mdb /unitcode 03``` will save all drawings located beneath the unit with unit code 03

Note that the parameters ```/pbs``` and ```/unitcode``` must be used separately—use either ```/pbs``` or ```/unitcode```, but not both.

The attribute configuration ini file, used in the command line with the
parameter ```/config```, can be created using the menu **File - Save settings
to file**.

The options of **\"Extract Drawing Files\** and **\"Extract From-To relations
for InlineComp_Prun\** from the interactive mode are saved in the file

```
%APPDATA%\CAXperts\PID ReportAdapter\config.ini
```

(e.g. ```C:\Documents and Settings\UserName\Application Data\CAXperts\PID ReportAdapter\config.ini```).
For the command line mode, the two options will be read from the
```config.ini``` and will be used for the extraction.

---

# Distribution Server — OpenID Connect with Azure Blob Storage

# OpenID Connect with Azure Blob Storage

**This approach is not recommended by CAXperts. Please use UDiTH Portal instead. This website will not be updated and potentially removed in future**

OpenID Connect is an authentication protocol built on OAuth 2.0 that allows an application to request access on behalf of an end user.

As this document is intended to provide only a basic overview, refer to the official documentation for further information, for example:
<https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols>.

In short, OpenID Connect is a protocol for providing SSO, single sign-on, by using the native browser through a common identity provider, the **Authorization Server**. It is built on top of OAuth 2.0 and allows an application to request access to a system on behalf of an end user. The **Client** is the application requesting access to the secured **Resource Server** on behalf of the end user, the **Resource Owner**. Any **Authorization Server** that supports OpenID Connect can, of course, be used in combination with UniversalPlantViewer. The **Resource Server** must support plain file access in a folder hierarchy, as opposed to an API such as Google Drive.

This example uses Azure AD in combination with Azure Blob Storage.


## High-level schema for Azure AD

<img src=".\media/OpenIDConnect_image2.png" width="652" height="441">

Conceptually, UniversalPlantViewer does not handle any user secret. The **Authorization Server** handles the sign-in independently and issues a token that can be used by UniversalPlantViewer to access the protected resource.

Therefore, it is not possible for the **Client**, UniversalPlantViewer, to leak a user password. Only the token could become a target for attacks and, if stolen, could be used to access the **Resource Server** for a limited period.

Refer to the **Authorization Server** documentation for further security measures, for example Multi-Factor Authentication or specific authorisation rules.

## Configuration in UniversalPlantViewer

UniversalPlantViewer uses the `IdentityModel.OidcClient` library internally, currently version `2.9.0.0`. Refer to the library options for further details.

The following extensions to the library's options are available:

-   **FrontChannelExtraParameters**: set additional request-query parameters for the authentication request, for example the resource parameter
-   **BackChannelExtraParameters**
-   **AuthorizationHeaders**: set static header values that should be sent when authenticated
-   **EnableLog**: when enabled, includes the `OidcClient2` log in the application

The configuration-file example for Azure is shown below. Missing configuration options use the library default values.

The **EnableLog** property should be enabled while setting up the configuration. Authentication problems are then logged to the general UniversalPlantViewer log.

After configuration has been completed, it is advisable to disable this setting so that security-relevant information is not written permanently to the log file.

## authenticationConfig.json

```json
{
  "Oidc": {
    "FrontChannelExtraParameters": {
      "resource": "https://upv.blob.core.windows.net"
    },
    "AuthorizationHeaders": {
      "x-ms-version": "2017-11-09"
    },
    "EnableLog": true,
    "Authority": "https://sts.windows.net/e1765baf-2fe4-4b9e-87ee-d130dde50afa",
    "ClientId": "5c2f88a0-766f-464d-bdad-d40ec1d4fb33",
    "Scope": "openid profile offline_access https://storage.azure.com/user_impersonation",
    "RedirectUri": "upvapi://auth",
    "RefreshDiscoveryDocumentForLogin": true,
    "RefreshDiscoveryOnSignatureFailure": false,
    "ResponseMode": 1,
    "LoadProfile": false,
    "Flow": 0,
    "TokenClientAuthenticationStyle": 1,
    "Policy": {
      "Discovery": {
        "ValidateEndpoints": false
      },
      "RequireAccessTokenHash": false,
      "RequireIdentityTokenOnRefreshTokenResponse": false
    }
  }
}
```

The following options must be adjusted to your environment:

-   **Resource**: the name of the blob storage

-   **Authority**: the Microsoft authorisation endpoint for your organisation, using for example the Directory, tenant, ID shown below. Refer to the Microsoft documentation for further options.

-   **ClientId**: the identifier for the application registration, that is, the Application, client, ID shown below

Azure-specific settings:

-   **ValidateEndpoints**: set this to `false` when the endpoint is on a different host from the authority

-   **AuthorizationHeader**: `x-ms-version`, determines the Azure API version

-   **RequireAccessTokenHash**: `false`

-   **LoadProfile**: `false`, not required by UniversalPlantViewer and requires additional permission

The required configuration IDs can be found in the app registrations overview:

![](.\media/OpenIDConnect_image3.png)

The configuration file is recognised automatically in the `Data` folder of the UniversalPlantViewer model at `Data\authenticationConfig.json`. It must be accessible without authentication, anonymous access.

It is not possible to set individual files to anonymous access in Azure Blob Storage.

As a workaround, the file can be stored in any other location, for example in a separate blob-storage container with global anonymous access. The file name does not matter in this context.

The user must open the model once by using a `upvapi://` link such as the one below. The configuration is then stored on the user device and recognised automatically for later attempts in the specified domain.

upvapi://https://upv.blob.core.windows.net/\$web/demoplantModel/?CMD!SetAuthConfig=https%3A%2F%2Fupv.blob.core.windows.net%2F%24web%2FdemoplantModel%2F!https%3A%2F%2Fupv.blob.core.windows.net%2FauthenticationConfig.json

1)  <https://upv.blob.core.windows.net/$web/demoplantModel/>  
    → model location for opening directly through the link

2)  `SetAuthConfig` parameter 1  
    `https%3A%2F%2Fupv.blob.core.windows.net%2F%24web%2FdemoplantModel%2F`  
    → domain for which the configuration is relevant, subroutes use the provided configuration when no other configuration is specified

3)  `SetAuthConfig` parameter 2  
    `https%3A%2F%2Fupv.blob.core.windows.net%2FauthenticationConfig.json`  
    → path to the configuration file, which must be accessible without authentication, supports file, web, or URI paths

The parameters of `SetAuthConfig` must be URL-encoded.

## Configuration in Azure

## Azure Blob storage

For a general overview see
<https://docs.microsoft.com/en-us/azure/storage/blobs/>

### Set up a storage account

<https://docs.microsoft.com/en-us/azure/storage/common/storage-account-create?tabs=azure-portal>

### Set up the containers

<https://docs.microsoft.com/en-us/azure/storage/blobs/storage-quickstart-blobs-portal>

You need one container for the configuration file. Set the access level to anonymous.

Upload the UniversalPlantViewer configuration file there.

You need another container for the secured UniversalPlantViewer model data. Set the access level to private.

Uploading multiple files or folders can be done by using Azure Storage Explorer.

<https://docs.microsoft.com/en-us/azure/vs-azure-tools-storage-explorer-blobs>

### Add user permissions

<https://docs.microsoft.com/en-us/azure/storage/common/storage-auth-aad-rbac-portal>

For the container you will have to set up user and group permissions.

To allow delegated access to the storage account, assign the **Storage Blob Data Reader** role for read-only access.

Depending on your requirements, other roles in the **Storage Blob Data** group can also be used.

![](.\media/OpenIDConnect_image4.png)

## App registration in Azure Active Directory

<https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app>

Set **Redirect URI** to `upvapi://auth`.

The Redirect URI must match the one provided in the configuration file.

A client secret is not required because UniversalPlantViewer is a public client.

### Add required permission

![](.\media/OpenIDConnect_image5.png)
UniversalPlantViewer needs to access the storage on behalf of the user. Therefore, add the following permission to the registration: **Azure Storage -- user_impersonation**.

Optionally, admin consent can be granted so that end users no longer need to provide consent individually.

### Add relay page (optional)

In Chrome, some calls to `upvapi://` were observed to be ignored silently when the browser does not have focus, which can happen, for example, when the user is already signed in.

Chrome treats this as a security feature.

In addition, there is no indicator in the browser when the sign-in has completed, and the browser window may be closed too early.

As a workaround, a relay web page can be used as an additional Redirect URI, see the attached example page.

![](.\media/OpenIDConnect_image6.png)

The Redirect URI in the UniversalPlantViewer configuration file must be adjusted accordingly.

## loginRedirect.html (example code)
```html
<html>
    <head>
        <title>UniversalPlantViewer Login</title>
    </head> 
    <body>
        <p>
            UniversalPlantViewer login should be succeeded <br>
            You can close this window now. If there is a problem, you can reissue the login request with below link.
        </p>
        <a id="#authLink">Login</a>
    </body>
    <script>
        var url = 'upvapi://auth' + window.location.search;
        function login() {
            window.location.assign(url);
        }
        window.onload = function() {
            var link = document.getElementById('#authLink');
            link.href = url;
            login();
        }
    </script>
</html>
```

### Case sensitivity of urls

URLs in Azure Blob Storage are, unlike many other systems, case-sensitive. Therefore, the file name capitalisation in the UPV model must remain unchanged.

The following capitalisation must also be considered for additional configuration files:

-   defaultConfig.upv
-   authenticationConfig.json
-   volumes.xls/xlsx
-   attributes.xls/xlsx
-   intelliPidAttributes.xls/xlsx
-   attributeData.xlsx
-   intelliPidAttributeData.xlsx
-   links.xls/xlsx/txt
-   intelliPidLinks.xls/xlsx/txt
-   defaultPackages.json/xlsx
-   upvcolorindex.txt
-   ReportDefinition.xls/xlsx
-   intelliPidReportDefinition.xls/xlsx
-   upvobjectsindex.txt
-   upvsketchitemindex.txt
-   upvintellipidsketchitemindex.txt
-   Measurements.xlsx
-   Endpreps.xlsx
-   projectMessage.png/jpg/jpeg

---

# CAXperts Licence Portal — CAXperts Licence Portal

# CAXperts Licence Portal

## Requesting access

To request access, please contact CAXperts at <support@caxperts.com>.

Please include the following details for each user who needs access:

- Email address
- First name
- Last name

Once your login has been issued, you will receive an invitation email similar to the one shown below:

![Example invitation email](./media/image7.png)

Select **SIGN UP** to register your account and create a password.

## Opening the portal

1. Go to <https://licensing.caxperts.com/>.
2. Log in using your credentials.

## Licence overview

All customer licences are listed under the **Licences** section of the portal.

![Licences overview](./media/image8.png)

Depending on the licence type, additional actions and pages may be available.

| Item | Icon | Description |
| --- | --- | --- |
| Assignments | ![Assignments icon](./media/image9.png) | Opens the list of device assignments for a licence. |
| Sessions | ![Sessions icon](./media/image10.png) | Opens the list of active sessions (floating licences only). |
| Copy licence key | ![Copy licence key icon](./media/image1.png) | Copies the licence key to the clipboard. |

## Assignments

The **Assignments** view shows the relationship between a licence and the devices that are using it.

| Action | Icon | Description |
| --- | --- | --- |
| Copy internal token ID | ![Copy token ID icon](./media/image2.png) | Copies the internal token ID. |
| Show internal token ID | ![Show token ID icon](./media/image11.png) | Displays the internal token ID. |
| Name an activation | ![Name activation icon](./media/image3.png) | Assigns a friendly name to the activation. |
| Suspend / unsuspend a device | ![Suspend icon](./media/image4.png) ![Unsuspend icon](./media/image12.png) | Suspends or unsuspends a device (see below). |
| Copy client ID | ![Copy client ID icon](./media/image13.png) | Copies the client ID. |
| Unassign / delete activation | ![Unassign icon](./media/image14.png) | Unassigns (deletes) the activation. |
| View device heartbeats | ![Heartbeats icon](./media/image15.png) | Shows the heartbeats for the device. |

### Suspending a device

Select the suspend button ![Suspend button](./media/image16.png) to block a device for a defined time period. While suspended, all licence requests from that device will be rejected.

![Suspend device dialogue](./media/image17.png)

To remove the block, select the **unsuspend** action.

## Sessions

The **Sessions** view shows currently open sessions. This is only available for floating licences.

| Action | Icon | Description |
| --- | --- | --- |
| Close a session | ![Close session icon](./media/image18.png) | Closes a single session. |
| Close all sessions | ![Close all sessions icon](./media/image19.png) | Closes all sessions for the licence. |

## Session analytics

To view analytics, open the **Sessions** page and select the **Analytics** tab.

![Analytics tab](./media/image20.png)

On this page you can set a date range and view reports.

![Analytics reports](./media/image21.png)

| Action | Icon | Description |
| --- | --- | --- |
| Zoom in | ![Zoom icon](./media/image22.png) | Zooms into the selected area of the chart. |
| Open details | ![Details icon](./media/image6.png) | Opens the details view for the report. |
| Settings | ![Settings icon](./media/image23.png) | Opens report settings. |

To download a report, select the download icon:

![Download icon](./media/image24.png)

This opens a detailed view where you can export the data.

![Export options](./media/image25.png)

| Format | Icon | Description |
| --- | --- | --- |
| CSV | ![CSV download icon](./media/image5.png) | Downloads the data as a CSV file. |
| Excel | ![Excel download icon](./media/image26.png) | Downloads the data as an Excel file. |

## UDiTH components and floating licences

### UDiTH Builder / Adapters

Running UDiTH Builder/Adapters does not open a session.

### UDiTH

Each UDiTH session (starting the software) consumes one token from the UDiTH licence.

If a licence is borrowed (checked out), one token remains blocked until the licence is returned.

## Support

For any questions, please contact <support@caxperts.com>.

---

