Implement LmxOpcUa server — all 6 phases complete
Full OPC UA server on .NET Framework 4.8 (x86) exposing AVEVA System Platform Galaxy tags via MXAccess. Mirrors Galaxy object hierarchy as OPC UA address space, translating contained-name browse paths to tag-name runtime references. Components implemented: - Configuration: AppConfiguration with 4 sections, validator - Domain: ConnectionState, Quality, Vtq, MxDataTypeMapper, error codes - MxAccess: StaComThread, MxAccessClient (partial classes), MxProxyAdapter using strongly-typed ArchestrA.MxAccess COM interop - Galaxy Repository: SQL queries (hierarchy, attributes, change detection), ChangeDetectionService with auto-rebuild on deploy - OPC UA Server: LmxNodeManager (CustomNodeManager2), LmxOpcUaServer, OpcUaServerHost with programmatic config, SecurityPolicy None - Status Dashboard: HTTP server with HTML/JSON/health endpoints - Integration: Full 14-step startup, graceful shutdown, component wiring 175 tests (174 unit + 1 integration), all passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
29
.gitignore
vendored
Normal file
29
.gitignore
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Build outputs
|
||||||
|
bin/
|
||||||
|
obj/
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vs/
|
||||||
|
.idea/
|
||||||
|
*.user
|
||||||
|
*.suo
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs/
|
||||||
|
|
||||||
|
# OS
|
||||||
|
Thumbs.db
|
||||||
|
desktop.ini
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# NuGet
|
||||||
|
packages/
|
||||||
|
*.nupkg
|
||||||
|
|
||||||
|
# Certificates
|
||||||
|
*.pfx
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# OPC sample server (large external repo)
|
||||||
|
tools/opcsampleserver/
|
||||||
113
CLAUDE.md
Normal file
113
CLAUDE.md
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Project Goal
|
||||||
|
|
||||||
|
Build an OPC UA server on .NET Framework 4.8 (32-bit) that exposes AVEVA System Platform (Wonderware) Galaxy tags via the MXAccess toolkit. The server mirrors the Galaxy object hierarchy as an OPC UA address space, translating between contained-name browse paths and tag-name runtime references.
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
### Data Flow
|
||||||
|
|
||||||
|
1. **Galaxy Repository DB (ZB)** — SQL Server database holding the deployed object hierarchy and attribute definitions. Queried at startup and on change detection to build/rebuild the OPC UA address space.
|
||||||
|
2. **MXAccess COM API** — Runtime data access layer. Subscribes to Galaxy tag attributes for live read/write. Requires a dedicated STA thread with a Win32 message pump for COM callbacks.
|
||||||
|
3. **OPC UA Server** — Exposes the hierarchy as browse nodes and attributes as variable nodes. Clients browse via contained names but reads/writes are translated to `tag_name.AttributeName` format for MXAccess.
|
||||||
|
|
||||||
|
### Key Concept: Contained Name vs Tag Name
|
||||||
|
|
||||||
|
Galaxy objects have two names:
|
||||||
|
- **contained_name** — human-readable name scoped to parent (used for OPC UA browse tree)
|
||||||
|
- **tag_name** — globally unique system name (used for MXAccess read/write)
|
||||||
|
|
||||||
|
Example: browsing `TestMachine_001/DelmiaReceiver/DownloadPath` translates to MXAccess reference `DelmiaReceiver_001.DownloadPath`.
|
||||||
|
|
||||||
|
See `gr/layout.md` for the full mapping and target OPC UA structure.
|
||||||
|
|
||||||
|
### Data Type Mapping
|
||||||
|
|
||||||
|
Galaxy `mx_data_type` values map to OPC UA types (Boolean, Int32, Float, Double, String, DateTime, etc.). Array attributes use ValueRank=1 with ArrayDimensions from the Galaxy attribute definition. Full mapping in `gr/data_type_mapping.md`.
|
||||||
|
|
||||||
|
### Change Detection
|
||||||
|
|
||||||
|
Poll `galaxy.time_of_last_deploy` in the ZB database to detect redeployments, then rebuild the address space. See `gr/build_layout_plan.md` for the step-by-step plan.
|
||||||
|
|
||||||
|
## Reference Implementation
|
||||||
|
|
||||||
|
An existing MXAccess client implementation is at:
|
||||||
|
`C:\Users\dohertj2\Desktop\scadalink-design\lmxproxy\src\ZB.MOM.WW.LmxProxy.Host`
|
||||||
|
|
||||||
|
Key patterns from that codebase:
|
||||||
|
- **StaComThread** — Dedicated STA thread with Win32 message pump (`GetMessage`/`DispatchMessage` loop). All MXAccess COM objects must be created and called on this thread. Uses `PostThreadMessage(WM_APP)` to marshal work items.
|
||||||
|
- **LMXProxyServer COM object** — `Register(clientName)` returns a connection handle. `AddItem(handle, address)` + `AdviseSupervisory(handle, itemHandle)` for subscriptions. `OnDataChange`/`OnWriteComplete` events for callbacks.
|
||||||
|
- **Reconnect** — Stored subscriptions are replayed after reconnect. A probe tag subscription monitors connection health.
|
||||||
|
- **COM cleanup** — `Marshal.ReleaseComObject()` on disconnect. Event handlers must be unwired before unregister.
|
||||||
|
|
||||||
|
## MXAccess Documentation
|
||||||
|
|
||||||
|
`mxaccess_documentation.md` in the project root contains the full ArchestrA MXAccess Toolkit User's Guide. Key API: `ArchestrA.MxAccess` namespace, `LMXProxyServer` class. The toolkit DLLs are in `Program Files (x86)\ArchestrA\Framework\bin`.
|
||||||
|
|
||||||
|
## Galaxy Repository Database
|
||||||
|
|
||||||
|
Connection: `sqlcmd -S localhost -d ZB -E` (Windows Auth). See `gr/connectioninfo.md`.
|
||||||
|
|
||||||
|
The `gr/` folder contains:
|
||||||
|
- `queries/` — SQL for hierarchy extraction, attribute lookup, and change detection
|
||||||
|
- `ddl/tables/` and `ddl/views/` — Schema definitions
|
||||||
|
- `schema.md` — Full table/view reference
|
||||||
|
- `build_layout_plan.md` — Step-by-step plan for building the OPC UA address space from DB queries
|
||||||
|
- `gr/CLAUDE.md` — Detailed guidance for working within the `gr/` subfolder
|
||||||
|
|
||||||
|
Key tables: `gobject` (hierarchy/deployment), `template_definition` (object categories), `dynamic_attribute` (user-defined attributes), `primitive_instance` (primitive-to-attribute links), `galaxy` (change detection).
|
||||||
|
|
||||||
|
## Build Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet restore ZB.MOM.WW.LmxOpcUa.slnx
|
||||||
|
dotnet build ZB.MOM.WW.LmxOpcUa.slnx
|
||||||
|
dotnet test ZB.MOM.WW.LmxOpcUa.slnx # all tests
|
||||||
|
dotnet test tests/ZB.MOM.WW.LmxOpcUa.Tests # unit tests only
|
||||||
|
dotnet test tests/ZB.MOM.WW.LmxOpcUa.IntegrationTests # integration tests only
|
||||||
|
dotnet test --filter "FullyQualifiedName~MyTestClass.MyMethod" # single test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build & Runtime Constraints
|
||||||
|
|
||||||
|
- Language: C#, .NET Framework 4.8, **x86 (32-bit)** platform target — required for MXAccess COM interop
|
||||||
|
- MXAccess requires a deployed ArchestrA Platform on the machine running the server
|
||||||
|
- COM apartment: MXAccess objects must live on an STA thread with a message pump
|
||||||
|
|
||||||
|
## Library Preferences
|
||||||
|
|
||||||
|
- **Logging**: Serilog with rolling daily file sink
|
||||||
|
- **Unit tests**: xUnit + Shouldly for assertions
|
||||||
|
- **Service hosting**: TopShelf (Windows service install/uninstall/run as console)
|
||||||
|
- **OPC UA**: OPC Foundation UA .NET Standard stack (https://github.com/opcfoundation/ua-.netstandard) — NuGet: `OPCFoundation.NetStandard.Opc.Ua.Server`
|
||||||
|
|
||||||
|
## OPC UA .NET Standard Documentation
|
||||||
|
|
||||||
|
Use the DeepWiki MCP (`mcp__deepwiki`) to query documentation for the OPC UA .NET Standard stack: `https://deepwiki.com/OPCFoundation/UA-.NETStandard`. Tools: `read_wiki_structure`, `read_wiki_contents`, and `ask_question` with repo `OPCFoundation/UA-.NETStandard`.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Use the dotnet OPC UA CLI tool at `tools/opcuacli-dotnet/` for manual testing against the running OPC UA server. Supports connect, read, write, subscribe, and browse commands. See `tools/opcuacli-dotnet/README.md` for usage details.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd tools/opcuacli-dotnet
|
||||||
|
dotnet run -- connect -u opc.tcp://localhost:4840
|
||||||
|
dotnet run -- browse -u opc.tcp://localhost:4840 -r -d 3
|
||||||
|
dotnet run -- read -u opc.tcp://localhost:4840 -n "ns=2;s=SomeNode"
|
||||||
|
dotnet run -- subscribe -u opc.tcp://localhost:4840 -n "ns=2;s=SomeNode" -i 500
|
||||||
|
```
|
||||||
|
|
||||||
|
### OPC PLC Sample Server
|
||||||
|
|
||||||
|
A test OPC UA server is available at `tools/opcsampleserver/` (Azure IoT OPC PLC). It generates simulated data nodes (slow, fast, anomaly, GUID) on `opc.tcp://localhost:50000`. Must run from the `publish` directory or use the provided batch scripts. Requires `--unsecuretransport` flag for the CLI tool to connect. See `tools/opcsampleserver/README.md` for full details.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start the test server
|
||||||
|
cd tools/opcsampleserver/publish && dotnet opcplc.dll --pn=50000 --autoaccept --unsecuretransport --sn=5 --sr=10 --st=uint --fn=5 --fr=1 --ft=uint
|
||||||
|
|
||||||
|
# Or use the batch script
|
||||||
|
tools/opcsampleserver/start-server.bat
|
||||||
|
```
|
||||||
9
ZB.MOM.WW.LmxOpcUa.slnx
Normal file
9
ZB.MOM.WW.LmxOpcUa.slnx
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<Solution>
|
||||||
|
<Folder Name="/src/">
|
||||||
|
<Project Path="src/ZB.MOM.WW.LmxOpcUa.Host/ZB.MOM.WW.LmxOpcUa.Host.csproj" />
|
||||||
|
</Folder>
|
||||||
|
<Folder Name="/tests/">
|
||||||
|
<Project Path="tests/ZB.MOM.WW.LmxOpcUa.Tests/ZB.MOM.WW.LmxOpcUa.Tests.csproj" />
|
||||||
|
<Project Path="tests/ZB.MOM.WW.LmxOpcUa.IntegrationTests/ZB.MOM.WW.LmxOpcUa.IntegrationTests.csproj" />
|
||||||
|
</Folder>
|
||||||
|
</Solution>
|
||||||
BIN
dashboard.JPG
Normal file
BIN
dashboard.JPG
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 68 KiB |
384
docs/implementation-plan.md
Normal file
384
docs/implementation-plan.md
Normal file
@@ -0,0 +1,384 @@
|
|||||||
|
# Implementation Plan: LmxOpcUa Server — All 44 Requirements
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
The LmxOpcUa project is scaffolded (solution, projects, configs, requirements docs) but has no implementation beyond Program.cs and a stub OpcUaService.cs. This plan implements all 44 requirements across 6 phases, each with verification gates and wiring checks to ensure nothing is left unconnected.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
Five major components wired together in OpcUaService.cs:
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
||||||
|
│ Galaxy Repository│────>│ OPC UA Server │<───>│ OPC UA Clients │
|
||||||
|
│ (SQL queries) │ │ (address space) │ │ │
|
||||||
|
└─────────────────┘ └────────┬──────────┘ └─────────────────┘
|
||||||
|
│
|
||||||
|
┌────────┴──────────┐
|
||||||
|
│ MxAccessClient │
|
||||||
|
│ (STA + COM) │
|
||||||
|
└───────────────────┘
|
||||||
|
│
|
||||||
|
┌────────┴──────────┐
|
||||||
|
│ Status Dashboard │
|
||||||
|
│ (HTTP + metrics) │
|
||||||
|
└───────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
Reference implementation: `C:\Users\dohertj2\Desktop\scadalink-design\lmxproxy\src\ZB.MOM.WW.LmxProxy.Host\`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PHASE 1: Foundation — Domain Models, Configuration, Interfaces
|
||||||
|
|
||||||
|
**Reqs:** SVC-003, SVC-006 (partial), MXA-008 (interfaces), MXA-009, OPC-005, OPC-012 (partial), GR-005 (config)
|
||||||
|
|
||||||
|
### Files to Create
|
||||||
|
|
||||||
|
**Configuration/**
|
||||||
|
- `AppConfiguration.cs` — top-level holder for all config sections
|
||||||
|
- `OpcUaConfiguration.cs` — Port, EndpointPath, ServerName, GalaxyName, MaxSessions, SessionTimeoutMinutes
|
||||||
|
- `MxAccessConfiguration.cs` — ClientName, timeouts, concurrency, probe settings
|
||||||
|
- `GalaxyRepositoryConfiguration.cs` — ConnectionString, intervals, command timeout
|
||||||
|
- `DashboardConfiguration.cs` — Enabled, Port, RefreshIntervalSeconds
|
||||||
|
- `ConfigurationValidator.cs` — validate and log effective config at startup
|
||||||
|
|
||||||
|
**Domain/**
|
||||||
|
- `ConnectionState.cs` — enum: Disconnected, Connecting, Connected, Disconnecting, Error, Reconnecting
|
||||||
|
- `ConnectionStateChangedEventArgs.cs` — PreviousState, CurrentState, Message
|
||||||
|
- `Vtq.cs` — Value/Timestamp/Quality struct with factory methods
|
||||||
|
- `Quality.cs` — enum with Bad/Uncertain/Good families matching OPC DA codes
|
||||||
|
- `QualityMapper.cs` — MapFromMxAccessQuality(int) and MapToOpcUaStatusCode(Quality)
|
||||||
|
- `MxDataTypeMapper.cs` — MapToOpcUaDataType(int mxDataType), MapToClrType(int). Unknown defaults to String
|
||||||
|
- `MxErrorCodes.cs` — translate 1008/1012/1013 to human messages
|
||||||
|
- `GalaxyObjectInfo.cs` — DTO matching hierarchy.sql columns
|
||||||
|
- `GalaxyAttributeInfo.cs` — DTO matching attributes.sql columns
|
||||||
|
- `IMxAccessClient.cs` — interface: Connect, Disconnect, Subscribe, Read, Write, OnTagValueChanged delegate
|
||||||
|
- `IGalaxyRepository.cs` — interface: GetHierarchy, GetAttributes, GetLastDeployTime, TestConnection, OnGalaxyChanged event
|
||||||
|
- `IMxProxy.cs` — abstraction over LMXProxyServer COM object (enables testing without DLL)
|
||||||
|
|
||||||
|
**Metrics/**
|
||||||
|
- `PerformanceMetrics.cs` — ITimingScope, OperationMetrics (1000-entry rolling buffer), BeginOperation/GetStatistics. Adapt from reference.
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
- `ConfigurationLoadingTests.cs` — bind appsettings.json, verify defaults
|
||||||
|
- `MxDataTypeMapperTests.cs` — all 12 type mappings + unknown default
|
||||||
|
- `QualityMapperTests.cs` — boundary values (0, 63, 64, 191, 192)
|
||||||
|
- `MxErrorCodesTests.cs` — known codes + unknown
|
||||||
|
- `PerformanceMetricsTests.cs` — recording, P95, buffer eviction, empty state
|
||||||
|
|
||||||
|
### Verification Gate 1
|
||||||
|
- [ ] `dotnet build` — zero errors
|
||||||
|
- [ ] All Phase 1 tests pass
|
||||||
|
- [ ] Config binding loads all 4 sections from appsettings.json
|
||||||
|
- [ ] MxDataTypeMapper covers every row in `gr/data_type_mapping.md`
|
||||||
|
- [ ] Quality enum covers all reference impl values
|
||||||
|
- [ ] Builds WITHOUT ArchestrA.MxAccess.dll (interface-based, no COM refs in Phase 1)
|
||||||
|
- [ ] Every new file has doc-comment referencing requirement ID(s)
|
||||||
|
- [ ] IMxAccessClient has every method needed by OPC-007, OPC-008, OPC-009
|
||||||
|
- [ ] IGalaxyRepository has every method needed by GR-001 through GR-004
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PHASE 2: MxAccessClient — STA Thread and COM Interop
|
||||||
|
|
||||||
|
**Reqs:** MXA-001, MXA-002, MXA-003, MXA-004, MXA-005, MXA-006, MXA-007, MXA-008 (wiring)
|
||||||
|
|
||||||
|
### Files to Create
|
||||||
|
|
||||||
|
**MxAccess/**
|
||||||
|
- `StaComThread.cs` — adapt from reference. STA thread, Win32 message pump, RunAsync(Action)/RunAsync<T>(Func<T>), WM_APP dispatch
|
||||||
|
- `MxAccessClient.cs` — core partial class implementing IMxAccessClient. Fields: StaComThread, IMxProxy, handle, state, semaphores, maps
|
||||||
|
- `MxAccessClient.Connection.cs` — ConnectAsync (Register on STA), DisconnectAsync (cleanup per MXA-007), COM cleanup
|
||||||
|
- `MxAccessClient.Subscription.cs` — SubscribeAsync (AddItem+AdviseSupervisory), UnsubscribeAsync, ReplayStoredSubscriptions
|
||||||
|
- `MxAccessClient.ReadWrite.cs` — ReadAsync (subscribe-get-first-unsubscribe), WriteAsync (Write+OnWriteComplete), semaphore-limited, timeout, ITimingScope metrics
|
||||||
|
- `MxAccessClient.EventHandlers.cs` — OnDataChange (resolve handle→address, create Vtq, invoke callback, update probe), OnWriteComplete (complete TCS, translate errors)
|
||||||
|
- `MxAccessClient.Monitor.cs` — monitor loop (reconnect on disconnect, probe staleness→force reconnect), cancellable
|
||||||
|
- `MxProxyAdapter.cs` — wraps real LMXProxyServer COM object, forwards calls to IMxProxy interface
|
||||||
|
|
||||||
|
**Test Helpers (in Tests project):**
|
||||||
|
- `FakeMxProxy.cs` — implements IMxProxy, simulates connections/data changes for testing
|
||||||
|
|
||||||
|
### Design Decision: IMxProxy Abstraction
|
||||||
|
Code against `IMxProxy` interface (not `LMXProxyServer` directly). This allows testing without ArchestrA.MxAccess.dll. `MxProxyAdapter` wraps the real COM object at runtime.
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
- `StaComThreadTests.cs` — STA apartment verified, work item execution, dispose
|
||||||
|
- `MxAccessClientConnectionTests.cs` — state transitions, cleanup order
|
||||||
|
- `MxAccessClientSubscriptionTests.cs` — subscribe/unsubscribe, stored subscriptions, reconnect replay, OnDataChange→callback
|
||||||
|
- `MxAccessClientReadWriteTests.cs` — read returns value, read timeout, write completes on callback, write timeout, semaphore limiting
|
||||||
|
- `MxAccessClientMonitorTests.cs` — reconnect on disconnect, probe staleness
|
||||||
|
|
||||||
|
### Verification Gate 2
|
||||||
|
- [ ] Solution builds without ArchestrA.MxAccess.dll
|
||||||
|
- [ ] STA thread test proves work items execute on STA apartment
|
||||||
|
- [ ] Connection lifecycle: Disconnected→Connecting→Connected→Disconnecting→Disconnected
|
||||||
|
- [ ] Subscription replay: stored subscriptions replayed after simulated reconnect
|
||||||
|
- [ ] Read/Write: timeout behavior returns error within expected window
|
||||||
|
- [ ] Metrics: Read/Write record timing in PerformanceMetrics
|
||||||
|
- [ ] **WIRING CHECK:** OnDataChange callback reaches OnTagValueChanged delegate
|
||||||
|
- [ ] COM cleanup order: UnAdvise→RemoveItem→unwire events→Unregister→ReleaseComObject
|
||||||
|
- [ ] Error codes 1008/1012/1013 translate correctly in OnWriteComplete path
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PHASE 3: Galaxy Repository — SQL Queries and Change Detection
|
||||||
|
|
||||||
|
**Reqs:** GR-001, GR-002, GR-003, GR-004, GR-006, GR-007
|
||||||
|
|
||||||
|
### Files to Create
|
||||||
|
|
||||||
|
**GalaxyRepository/**
|
||||||
|
- `GalaxyRepositoryService.cs` — implements IGalaxyRepository. SQL embedded as `const string` (from gr/queries/). ADO.NET SqlConnection per-query. GetHierarchyAsync, GetAttributesAsync, GetLastDeployTimeAsync, TestConnectionAsync
|
||||||
|
- `ChangeDetectionService.cs` — background Timer at configured interval. Polls GetLastDeployTimeAsync, compares to last known, fires OnGalaxyChanged on change. First poll always triggers. Failed poll logs Warning, retries next interval
|
||||||
|
- `GalaxyRepositoryStats.cs` — POCO for dashboard: GalaxyName, DbConnected, LastDeployTime, ObjectCount, AttributeCount, LastRebuildTime
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
- `ChangeDetectionServiceTests.cs` — first poll triggers, same timestamp skips, changed triggers, failed poll retries
|
||||||
|
- `GalaxyRepositoryServiceTests.cs` (integration, in IntegrationTests) — TestConnection, GetHierarchy returns rows, GetAttributes returns rows
|
||||||
|
|
||||||
|
### Verification Gate 3
|
||||||
|
- [ ] All SQL is `const string` — no concatenation, no parameters, no INSERT/UPDATE/DELETE (GR-006 code review)
|
||||||
|
- [ ] GetHierarchyAsync maps all columns: gobject_id, tag_name, contained_name, browse_name, parent_gobject_id, is_area
|
||||||
|
- [ ] GetAttributesAsync maps all columns including array_dimension
|
||||||
|
- [ ] Change detection: first poll fires, same timestamp skips, changed fires
|
||||||
|
- [ ] Failed query does NOT crash or trigger false rebuild
|
||||||
|
- [ ] GalaxyRepositoryStats populated for dashboard
|
||||||
|
- [ ] Zero rows from hierarchy logs Warning
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PHASE 4: OPC UA Server — Address Space and Node Manager
|
||||||
|
|
||||||
|
**Reqs:** OPC-001, OPC-002, OPC-003, OPC-004, OPC-005, OPC-006, OPC-007, OPC-008, OPC-009, OPC-010, OPC-011, OPC-012, OPC-013
|
||||||
|
|
||||||
|
### Files to Create
|
||||||
|
|
||||||
|
**OpcUa/**
|
||||||
|
- `LmxOpcUaServer.cs` — inherits StandardServer. Creates custom node manager. SecurityPolicy None. Registers namespace `urn:{GalaxyName}:LmxOpcUa`
|
||||||
|
- `LmxNodeManager.cs` — inherits CustomNodeManager2. Core class:
|
||||||
|
- `BuildAddressSpace(hierarchy, attributes)` — creates folder/object/variable nodes from Galaxy data. NodeId: `ns=1;s={tag_name}` / `ns=1;s={tag_name}.{attr}`. Stores full_tag_reference lookup
|
||||||
|
- `RebuildAddressSpace(hierarchy, attributes)` — removes old nodes, rebuilds. Preserves sessions
|
||||||
|
- Read/Write overrides delegate to IMxAccessClient via stored full_tag_reference
|
||||||
|
- Subscription management: ref-counted shared MXAccess subscriptions
|
||||||
|
- `OpcUaServerHost.cs` — manages ApplicationInstance lifecycle. Programmatic config (no XML). Start/Stop. Exposes ActiveSessionCount
|
||||||
|
- `OpcUaQualityMapper.cs` — domain Quality → OPC UA StatusCodes
|
||||||
|
- `DataValueConverter.cs` — COM variant ↔ OPC UA DataValue. Handles all types from data_type_mapping.md. DateTime UTC. Arrays
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
- `DataValueConverterTests.cs` — all type conversions, arrays, DateTime UTC
|
||||||
|
- `LmxNodeManagerBuildTests.cs` — synthetic hierarchy matching gr/layout.md, verify node types, NodeIds, data types, ValueRank, ArrayDimensions
|
||||||
|
- `LmxNodeManagerRebuildTests.cs` — rebuild replaces nodes, old nodes gone, new nodes present
|
||||||
|
- `OpcUaQualityMapperTests.cs` — all quality families
|
||||||
|
|
||||||
|
### Verification Gate 4
|
||||||
|
- [ ] Endpoint URL: `opc.tcp://{hostname}:{port}/LmxOpcUa`
|
||||||
|
- [ ] Namespace: `urn:{GalaxyName}:LmxOpcUa` at index 1
|
||||||
|
- [ ] Root ZB folder under Objects
|
||||||
|
- [ ] Areas → FolderType + Organizes reference
|
||||||
|
- [ ] Non-areas → BaseObjectType + HasComponent reference
|
||||||
|
- [ ] Variable nodes: correct DataType, ValueRank, ArrayDimensions per data_type_mapping.md
|
||||||
|
- [ ] **WIRING CHECK:** Read handler resolves NodeId → full_tag_reference → calls IMxAccessClient.ReadAsync
|
||||||
|
- [ ] **WIRING CHECK:** Write handler resolves NodeId → full_tag_reference → calls IMxAccessClient.WriteAsync
|
||||||
|
- [ ] Rebuild removes old nodes, creates new ones without crash
|
||||||
|
- [ ] SecurityPolicy is None
|
||||||
|
- [ ] MaxSessions/SessionTimeout configured from appsettings
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PHASE 5: Status Dashboard — HTTP, HTML, JSON, Health
|
||||||
|
|
||||||
|
**Reqs:** DASH-001 through DASH-009
|
||||||
|
|
||||||
|
### Files to Create
|
||||||
|
|
||||||
|
**Status/**
|
||||||
|
- `StatusData.cs` — DTO: ConnectionInfo, HealthInfo, SubscriptionInfo, GalaxyInfo, OperationMetrics, Footer
|
||||||
|
- `HealthCheckService.cs` — rules: not connected→Unhealthy, success rate<50% w/>100 ops→Degraded, else Healthy
|
||||||
|
- `StatusReportService.cs` — aggregates from all components. GenerateHtml (self-contained, inline CSS, color-coded panels, meta-refresh). GenerateJson. IsHealthy
|
||||||
|
- `StatusWebServer.cs` — HttpListener. Routes: / → HTML, /api/status → JSON, /api/health → 200/503. GET only. no-cache headers. Disableable
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
- `HealthCheckServiceTests.cs` — three health rules, messages
|
||||||
|
- `StatusReportServiceTests.cs` — HTML contains all panels, JSON deserializes, meta-refresh tag
|
||||||
|
- `StatusWebServerTests.cs` — routing (200/405/404), cache headers, start/stop
|
||||||
|
|
||||||
|
### Verification Gate 5
|
||||||
|
- [ ] HTML contains all panels: Connection, Health, Subscriptions, Galaxy Info, Operations table, Footer
|
||||||
|
- [ ] Connection panel: green/red/yellow border per state
|
||||||
|
- [ ] Health panel: three states with correct colors
|
||||||
|
- [ ] Operations table: Read/Write/Subscribe/Browse with Count/SuccessRate/Avg/Min/Max/P95
|
||||||
|
- [ ] Galaxy Info panel: galaxy name, DB status, last deploy, object/attribute counts, last rebuild
|
||||||
|
- [ ] Footer: timestamp + assembly version
|
||||||
|
- [ ] JSON API: all same data as HTML
|
||||||
|
- [ ] /api/health: 200 when healthy, 503 when unhealthy
|
||||||
|
- [ ] Meta-refresh tag with configured interval
|
||||||
|
- [ ] Port conflict does not prevent service startup
|
||||||
|
- [ ] Dashboard disabled via config skips HttpListener
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PHASE 6: Integration Wiring and End-to-End Verification
|
||||||
|
|
||||||
|
**Reqs:** SVC-004, SVC-005, SVC-006, ALL wiring verification
|
||||||
|
|
||||||
|
### OpcUaService.cs — Full Implementation
|
||||||
|
|
||||||
|
**Start() sequence (SVC-005):**
|
||||||
|
1. Load AppConfiguration via IConfiguration
|
||||||
|
2. ConfigurationValidator.ValidateAndLog()
|
||||||
|
3. Register AppDomain.UnhandledException handler (SVC-006)
|
||||||
|
4. Create PerformanceMetrics
|
||||||
|
5. Create MxAccessClient → ConnectAsync (failure = fatal, don't start)
|
||||||
|
6. Start MxAccessClient monitor loop
|
||||||
|
7. Create GalaxyRepositoryService → TestConnectionAsync (failure = warning, continue)
|
||||||
|
8. Create OpcUaServerHost + LmxNodeManager, inject IMxAccessClient
|
||||||
|
9. Query initial hierarchy + attributes → BuildAddressSpace
|
||||||
|
10. Start OPC UA server listener (failure = fatal)
|
||||||
|
11. Create ChangeDetectionService → **wire OnGalaxyChanged → nodeManager.RebuildAddressSpace**
|
||||||
|
12. Start change detection polling
|
||||||
|
13. Create HealthCheckService, StatusReportService, StatusWebServer → Start (failure = warning)
|
||||||
|
14. Log "LmxOpcUa service started successfully"
|
||||||
|
|
||||||
|
**Critical wiring (GUARDRAILS):**
|
||||||
|
- `_mxAccessClient.OnTagValueChanged` → node manager subscription delivery
|
||||||
|
- `_changeDetectionService.OnGalaxyChanged` → `_nodeManager.RebuildAddressSpace`
|
||||||
|
- `_mxAccessClient.ConnectionStateChanged` → health check updates
|
||||||
|
- Node manager Read/Write → `_mxAccessClient.ReadAsync/WriteAsync`
|
||||||
|
- StatusReportService reads from: MxAccessClient, PerformanceMetrics, GalaxyRepositoryStats, OpcUaServerHost
|
||||||
|
|
||||||
|
**Stop() sequence (SVC-004, reverse order, 30s max):**
|
||||||
|
1. Cancel CancellationTokenSource (stops all background loops)
|
||||||
|
2. Stop change detection
|
||||||
|
3. Stop OPC UA server
|
||||||
|
4. Disconnect MXAccess (full COM cleanup)
|
||||||
|
5. Stop StatusWebServer
|
||||||
|
6. Dispose PerformanceMetrics
|
||||||
|
7. Log "Service shutdown complete"
|
||||||
|
|
||||||
|
### Wiring Verification Tests (GUARDRAILS)
|
||||||
|
|
||||||
|
These tests prove components are connected end-to-end, not just implemented in isolation:
|
||||||
|
|
||||||
|
- `Wiring/MxAccessToNodeManagerWiringTest.cs` — simulate OnDataChange on FakeMxProxy → verify data reaches node manager subscription delivery
|
||||||
|
- `Wiring/ChangeDetectionToRebuildWiringTest.cs` — mock GalaxyRepository returns changed timestamp → verify RebuildAddressSpace called
|
||||||
|
- `Wiring/OpcUaReadToMxAccessWiringTest.cs` — issue Read via NodeManager → verify FakeMxProxy receives correct full_tag_reference
|
||||||
|
- `Wiring/OpcUaWriteToMxAccessWiringTest.cs` — issue Write via NodeManager → verify FakeMxProxy receives correct tag + value
|
||||||
|
- `Wiring/ServiceStartupSequenceTest.cs` — create OpcUaService with fakes, call Start(), verify all components created and wired
|
||||||
|
- `Wiring/ShutdownCompletesTest.cs` — Start then Stop, verify completes within 30s
|
||||||
|
- `EndToEnd/FullDataFlowTest.cs` — **THE ULTIMATE SMOKE TEST**: full service with fakes, verify: (1) address space built, (2) MXAccess data change → OPC UA variable, (3) read → correct tag ref, (4) write → correct tag+value, (5) dashboard HTML has real data
|
||||||
|
|
||||||
|
### Verification Gate 6 (FINAL)
|
||||||
|
- [ ] Startup: all 14 steps execute in order
|
||||||
|
- [ ] Shutdown: completes within 30s, all components disposed in reverse order
|
||||||
|
- [ ] **WIRING:** MXAccess OnDataChange → node manager subscription delivery
|
||||||
|
- [ ] **WIRING:** Galaxy change → address space rebuild
|
||||||
|
- [ ] **WIRING:** OPC UA Read → MXAccess ReadAsync with correct tag reference
|
||||||
|
- [ ] **WIRING:** OPC UA Write → MXAccess WriteAsync with correct tag+value
|
||||||
|
- [ ] **WIRING:** Dashboard aggregates data from all components
|
||||||
|
- [ ] **WIRING:** Health endpoint reflects actual connection state
|
||||||
|
- [ ] AppDomain.UnhandledException registered
|
||||||
|
- [ ] TopShelf recovery configured (restart, 60s delay)
|
||||||
|
- [ ] FullDataFlowTest passes end-to-end
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Master Requirement Traceability (all 44)
|
||||||
|
|
||||||
|
| Req | Phase | Verified By |
|
||||||
|
|-----|-------|-------------|
|
||||||
|
| SVC-001 | Done | Program.cs already configured |
|
||||||
|
| SVC-002 | Done | Program.cs already configured |
|
||||||
|
| SVC-003 | 1 | ConfigurationLoadingTests |
|
||||||
|
| SVC-004 | 6 | ShutdownCompletesTest |
|
||||||
|
| SVC-005 | 6 | ServiceStartupSequenceTest |
|
||||||
|
| SVC-006 | 6 | AppDomain handler registration test |
|
||||||
|
| MXA-001 | 2 | StaComThreadTests |
|
||||||
|
| MXA-002 | 2 | MxAccessClientConnectionTests |
|
||||||
|
| MXA-003 | 2 | MxAccessClientSubscriptionTests |
|
||||||
|
| MXA-004 | 2 | MxAccessClientReadWriteTests |
|
||||||
|
| MXA-005 | 2 | MxAccessClientMonitorTests |
|
||||||
|
| MXA-006 | 2 | MxAccessClientMonitorTests (probe) |
|
||||||
|
| MXA-007 | 2 | Cleanup order test |
|
||||||
|
| MXA-008 | 2 | Metrics integration in ReadWrite |
|
||||||
|
| MXA-009 | 1+2 | MxErrorCodesTests + write error path |
|
||||||
|
| GR-001 | 3 | GetHierarchyAsync maps all columns |
|
||||||
|
| GR-002 | 3 | GetAttributesAsync maps all columns |
|
||||||
|
| GR-003 | 3 | ChangeDetectionServiceTests |
|
||||||
|
| GR-004 | 3+6 | ChangeDetectionToRebuildWiringTest |
|
||||||
|
| GR-005 | 1+3 | Config tests + ADO.NET usage |
|
||||||
|
| GR-006 | 3 | Code review: const string SQL only |
|
||||||
|
| GR-007 | 3 | TestConnectionAsync test |
|
||||||
|
| OPC-001 | 4 | Endpoint URL test |
|
||||||
|
| OPC-002 | 4 | BuildTests: node types + references |
|
||||||
|
| OPC-003 | 4 | BuildTests: variable nodes |
|
||||||
|
| OPC-004 | 4+6 | ReadWiringTest: browse→tag_name |
|
||||||
|
| OPC-005 | 1+4 | MxDataTypeMapperTests + variable node DataType |
|
||||||
|
| OPC-006 | 4 | BuildTests: ValueRank + ArrayDimensions |
|
||||||
|
| OPC-007 | 4+6 | OpcUaReadToMxAccessWiringTest |
|
||||||
|
| OPC-008 | 4+6 | OpcUaWriteToMxAccessWiringTest |
|
||||||
|
| OPC-009 | 4+6 | MxAccessToNodeManagerWiringTest |
|
||||||
|
| OPC-010 | 4+6 | RebuildTests + ChangeDetectionToRebuildWiringTest |
|
||||||
|
| OPC-011 | 4 | ServerStatus node test |
|
||||||
|
| OPC-012 | 4 | Namespace URI test |
|
||||||
|
| OPC-013 | 4 | Session config test |
|
||||||
|
| DASH-001 | 5 | StatusWebServerTests routing |
|
||||||
|
| DASH-002 | 5 | HTML contains Connection panel |
|
||||||
|
| DASH-003 | 5 | HealthCheckServiceTests |
|
||||||
|
| DASH-004 | 5 | HTML contains Subscriptions panel |
|
||||||
|
| DASH-005 | 5 | HTML contains Operations table |
|
||||||
|
| DASH-006 | 5 | HTML contains Footer |
|
||||||
|
| DASH-007 | 5 | Meta-refresh tag test |
|
||||||
|
| DASH-008 | 5 | JSON API deserialization test |
|
||||||
|
| DASH-009 | 5 | HTML contains Galaxy Info panel |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Final Folder Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
src/ZB.MOM.WW.LmxOpcUa.Host/
|
||||||
|
Configuration/ (Phase 1)
|
||||||
|
Domain/ (Phase 1)
|
||||||
|
Metrics/ (Phase 1)
|
||||||
|
MxAccess/ (Phase 2)
|
||||||
|
GalaxyRepository/ (Phase 3)
|
||||||
|
OpcUa/ (Phase 4)
|
||||||
|
Status/ (Phase 5)
|
||||||
|
OpcUaService.cs (Phase 6 — full wiring)
|
||||||
|
Program.cs (existing)
|
||||||
|
appsettings.json (existing)
|
||||||
|
tests/ZB.MOM.WW.LmxOpcUa.Tests/
|
||||||
|
Configuration/ (Phase 1)
|
||||||
|
Domain/ (Phase 1)
|
||||||
|
Metrics/ (Phase 1)
|
||||||
|
MxAccess/ (Phase 2)
|
||||||
|
GalaxyRepository/ (Phase 3)
|
||||||
|
OpcUa/ (Phase 4)
|
||||||
|
Status/ (Phase 5)
|
||||||
|
Wiring/ (Phase 6 — GUARDRAILS)
|
||||||
|
EndToEnd/ (Phase 6 — GUARDRAILS)
|
||||||
|
Helpers/FakeMxProxy.cs (Phase 2)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verification: How to Run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build
|
||||||
|
dotnet build ZB.MOM.WW.LmxOpcUa.slnx
|
||||||
|
|
||||||
|
# All tests
|
||||||
|
dotnet test ZB.MOM.WW.LmxOpcUa.slnx
|
||||||
|
|
||||||
|
# Phase-specific (by namespace convention)
|
||||||
|
dotnet test tests/ZB.MOM.WW.LmxOpcUa.Tests --filter "FullyQualifiedName~Configuration"
|
||||||
|
dotnet test tests/ZB.MOM.WW.LmxOpcUa.Tests --filter "FullyQualifiedName~MxAccess"
|
||||||
|
dotnet test tests/ZB.MOM.WW.LmxOpcUa.Tests --filter "FullyQualifiedName~GalaxyRepository"
|
||||||
|
dotnet test tests/ZB.MOM.WW.LmxOpcUa.Tests --filter "FullyQualifiedName~OpcUa"
|
||||||
|
dotnet test tests/ZB.MOM.WW.LmxOpcUa.Tests --filter "FullyQualifiedName~Status"
|
||||||
|
dotnet test tests/ZB.MOM.WW.LmxOpcUa.Tests --filter "FullyQualifiedName~Wiring"
|
||||||
|
dotnet test tests/ZB.MOM.WW.LmxOpcUa.Tests --filter "FullyQualifiedName~EndToEnd"
|
||||||
|
|
||||||
|
# Integration tests (requires ZB database)
|
||||||
|
dotnet test tests/ZB.MOM.WW.LmxOpcUa.IntegrationTests
|
||||||
|
```
|
||||||
121
docs/reqs/GalaxyRepositoryReqs.md
Normal file
121
docs/reqs/GalaxyRepositoryReqs.md
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
# Galaxy Repository — Component Requirements
|
||||||
|
|
||||||
|
Parent: [HLR-002](HighLevelReqs.md#hlr-002-galaxy-hierarchy-as-opc-ua-address-space), [HLR-005](HighLevelReqs.md#hlr-005-dynamic-address-space-rebuild)
|
||||||
|
|
||||||
|
## GR-001: Hierarchy Extraction
|
||||||
|
|
||||||
|
The system shall query the Galaxy Repository database to extract all deployed objects with their parent-child containment relationships, contained names, and tag names.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Executes `queries/hierarchy.sql` against the ZB database.
|
||||||
|
- Returns a list of objects with: `gobject_id`, `tag_name`, `contained_name`, `browse_name`, `parent_gobject_id`, `is_area`.
|
||||||
|
- Objects with `parent_gobject_id = 0` are children of the root ZB node.
|
||||||
|
- Only deployed, non-template objects matching the category filter (areas, engines, user-defined objects, etc.) are returned.
|
||||||
|
- Query completes within 10 seconds on a typical Galaxy (hundreds of objects). Log a Warning if it takes longer.
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Results are ordered by `parent_gobject_id, tag_name` for deterministic tree building.
|
||||||
|
- If the query returns zero rows, log a Warning (Galaxy may have no deployed objects, or the DB connection may be misconfigured).
|
||||||
|
- Orphan detection: if a row references a `parent_gobject_id` that does not exist in the result set and is not 0, log a Warning and skip that node.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## GR-002: Attribute Extraction
|
||||||
|
|
||||||
|
The system shall query user-defined (dynamic) attributes for deployed objects, including data type, array flag, and array dimensions.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Executes `queries/attributes.sql` using the template chain CTE to resolve inherited attributes.
|
||||||
|
- Returns: `gobject_id`, `tag_name`, `attribute_name`, `full_tag_reference`, `mx_data_type`, `is_array`, `array_dimension`, `security_classification`.
|
||||||
|
- Attributes starting with `_` are filtered out by the query.
|
||||||
|
- `array_dimension` is correctly extracted from the `mx_value` hex bytes (positions 13-16, little-endian uint16).
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- CTE recursion depth is limited to 10 levels (per the query). This is sufficient for Galaxy template hierarchies.
|
||||||
|
- If `mx_data_type` is null or not in the known set (1-8, 13-16), default to String.
|
||||||
|
- If `gobject_id` from an attribute row does not match any hierarchy object, skip that attribute (object may not be deployed).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## GR-003: Change Detection
|
||||||
|
|
||||||
|
The system shall poll `galaxy.time_of_last_deploy` at a configurable interval to detect when a new deployment has occurred.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Polls `SELECT time_of_last_deploy FROM galaxy` at a configurable interval (`GalaxyRepository:ChangeDetectionIntervalSeconds`, default 30 seconds).
|
||||||
|
- Compares the returned timestamp to the last known value stored in memory.
|
||||||
|
- If different, triggers a rebuild (re-run hierarchy + attributes queries, notify OPC UA server).
|
||||||
|
- First poll after startup always triggers an initial build.
|
||||||
|
- If the query fails (SQL timeout, connection error), log Warning and retry at next interval. Do not trigger a rebuild on failure.
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Polling runs on a background timer thread, not blocking the STA thread.
|
||||||
|
- `time_of_last_deploy` is a datetime column. Compare using exact equality (not range).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## GR-004: Rebuild on Change
|
||||||
|
|
||||||
|
When a deployment change is detected, the system shall re-query hierarchy and attributes and provide the updated structure to the OPC UA server for address space rebuild.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- On change detection, re-query both hierarchy and attributes.
|
||||||
|
- Provide the new data set to the OPC UA server component for address space replacement.
|
||||||
|
- Log at Information level: "Galaxy deployment change detected. Rebuilding address space. ({ObjectCount} objects, {AttributeCount} attributes)".
|
||||||
|
- Log total rebuild time at Information level.
|
||||||
|
- If the re-query fails, log Error and keep the existing address space (do not clear it).
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Rebuild is not atomic from the DB perspective — hierarchy and attributes are two separate queries. This is acceptable; deployment is an infrequent operation.
|
||||||
|
- Raise an event/callback that the OPC UA server subscribes to: `OnGalaxyChanged(hierarchyData, attributeData)`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## GR-005: Connection Configuration
|
||||||
|
|
||||||
|
Database connection parameters shall be configurable via appsettings.json (connection string using Windows Authentication by default).
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Connection string in `appsettings.json` under `GalaxyRepository:ConnectionString`.
|
||||||
|
- Default: `Server=localhost;Database=ZB;Integrated Security=true` (Windows Auth).
|
||||||
|
- ADO.NET `SqlConnection` used for queries (.NET Framework 4.8 built-in).
|
||||||
|
- Connection is opened per-query (not kept open). Connection pooling handles efficiency.
|
||||||
|
- If the initial connection test at startup fails, log Error with the connection string and continue attempting (change detection polls will keep retrying).
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Command timeout: configurable via `GalaxyRepository:CommandTimeoutSeconds`, default 30 seconds.
|
||||||
|
- No ORM. Raw ADO.NET with `SqlCommand` and `SqlDataReader`. SQL text is embedded as constants (not dynamically constructed).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## GR-006: Query Safety
|
||||||
|
|
||||||
|
All SQL queries shall be static read-only SELECT statements. No writes to the Galaxy Repository database.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- All queries are hardcoded SQL strings with no string concatenation or user-supplied parameters.
|
||||||
|
- No INSERT, UPDATE, DELETE, or DDL statements are ever executed against the Galaxy database.
|
||||||
|
- Queries use only SELECT with read-only intent.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## GR-007: Startup Validation
|
||||||
|
|
||||||
|
On startup, the Galaxy Repository component shall validate database connectivity.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Execute a simple test query (`SELECT 1`) against the configured database.
|
||||||
|
- If the database is unreachable, log an Error but do not prevent service startup.
|
||||||
|
- The service runs in degraded mode (empty address space) until the database becomes available and the next change detection poll succeeds.
|
||||||
47
docs/reqs/HighLevelReqs.md
Normal file
47
docs/reqs/HighLevelReqs.md
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# High-Level Requirements
|
||||||
|
|
||||||
|
## HLR-001: OPC UA Server
|
||||||
|
|
||||||
|
The system shall expose an OPC UA server endpoint that OPC UA clients can connect to for browsing, reading, and writing Galaxy tag data.
|
||||||
|
|
||||||
|
## HLR-002: Galaxy Hierarchy as OPC UA Address Space
|
||||||
|
|
||||||
|
The system shall build an OPC UA address space that mirrors the System Platform Galaxy object hierarchy, using contained names for browse structure and tag names for runtime data access.
|
||||||
|
|
||||||
|
## HLR-003: MXAccess Runtime Data Access
|
||||||
|
|
||||||
|
The system shall use the MXAccess toolkit to subscribe to, read, and write Galaxy tag attribute values at runtime on behalf of connected OPC UA clients.
|
||||||
|
|
||||||
|
## HLR-004: Data Type Mapping
|
||||||
|
|
||||||
|
The system shall map Galaxy attribute data types (mx_data_type) to appropriate OPC UA built-in types, including support for array attributes.
|
||||||
|
|
||||||
|
## HLR-005: Dynamic Address Space Rebuild
|
||||||
|
|
||||||
|
The system shall detect Galaxy deployment changes (via `galaxy.time_of_last_deploy`) and rebuild the OPC UA address space to reflect the current deployed state.
|
||||||
|
|
||||||
|
## HLR-006: Windows Service Hosting
|
||||||
|
|
||||||
|
The system shall run as a Windows service (via TopShelf) with support for install, uninstall, and interactive console modes.
|
||||||
|
|
||||||
|
## HLR-007: Logging
|
||||||
|
|
||||||
|
The system shall log operational events to rolling daily log files using Serilog.
|
||||||
|
|
||||||
|
## HLR-008: Connection Resilience
|
||||||
|
|
||||||
|
The system shall automatically reconnect to MXAccess after connection loss, replaying active subscriptions upon reconnect.
|
||||||
|
|
||||||
|
## HLR-009: Status Dashboard
|
||||||
|
|
||||||
|
The system shall host an embedded HTTP status dashboard (similar to the LmxProxy dashboard) providing at-a-glance operational visibility including connection state, health, subscription statistics, and operation metrics.
|
||||||
|
|
||||||
|
## Component-Level Requirements
|
||||||
|
|
||||||
|
Detailed requirements are broken out into the following documents:
|
||||||
|
|
||||||
|
- [OPC UA Server Requirements](OpcUaServerReqs.md)
|
||||||
|
- [MXAccess Client Requirements](MxAccessClientReqs.md)
|
||||||
|
- [Galaxy Repository Requirements](GalaxyRepositoryReqs.md)
|
||||||
|
- [Service Host Requirements](ServiceHostReqs.md)
|
||||||
|
- [Status Dashboard Requirements](StatusDashboardReqs.md)
|
||||||
172
docs/reqs/MxAccessClientReqs.md
Normal file
172
docs/reqs/MxAccessClientReqs.md
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
# MXAccess Client — Component Requirements
|
||||||
|
|
||||||
|
Parent: [HLR-003](HighLevelReqs.md#hlr-003-mxaccess-runtime-data-access), [HLR-008](HighLevelReqs.md#hlr-008-connection-resilience)
|
||||||
|
|
||||||
|
## MXA-001: STA Thread with Message Pump
|
||||||
|
|
||||||
|
All MXAccess COM objects shall be created and called on a dedicated STA thread running a Win32 message pump to ensure COM callbacks are delivered.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- A dedicated thread is created with `ApartmentState.STA` before any MXAccess COM objects are instantiated.
|
||||||
|
- The thread runs a Win32 message pump using `GetMessage`/`TranslateMessage`/`DispatchMessage` loop.
|
||||||
|
- Work items are marshalled to the STA thread via `PostThreadMessage(WM_APP)` and a concurrent queue.
|
||||||
|
- The STA thread processes work items between message pump iterations.
|
||||||
|
- All COM object creation (`LMXProxyServer` constructor), method calls, and event callbacks happen on this thread.
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Thread name: `MxAccess-STA` (for diagnostics).
|
||||||
|
- If the STA thread dies unexpectedly, log Fatal and trigger service shutdown. Do not attempt to create a replacement thread (COM objects on the dead thread are unrecoverable).
|
||||||
|
- `RunAsync(Action)` method returns a `Task` that completes when the action executes on the STA thread. Callers can `await` it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MXA-002: Connection Lifecycle
|
||||||
|
|
||||||
|
The client shall support Register/Unregister lifecycle with the LMXProxyServer COM object, tracking the connection handle.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- `Register(clientName)` is called on the STA thread and returns a positive connection handle on success.
|
||||||
|
- If Register returns handle <= 0, throw with descriptive error.
|
||||||
|
- `Unregister(handle)` is called during disconnect after all subscriptions are removed.
|
||||||
|
- Client name: configurable via `MxAccess:ClientName`, default `LmxOpcUa`. Must be unique per MXAccess registration.
|
||||||
|
- Connection state transitions: Disconnected → Connecting → Connected → Disconnecting → Disconnected (and Error from any state).
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- `ConnectedSince` timestamp (UTC) is recorded after successful Register.
|
||||||
|
- `ReconnectCount` is tracked for diagnostics and dashboard display.
|
||||||
|
- State change events are raised for dashboard and health check consumption.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MXA-003: Tag Subscription
|
||||||
|
|
||||||
|
The client shall support subscribing to tags via AddItem + AdviseSupervisory, receiving value updates through OnDataChange callbacks.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Subscribe sequence: `AddItem(handle, address)` returns item handle, then `AdviseSupervisory(handle, itemHandle)` starts the subscription.
|
||||||
|
- `OnDataChange` callback delivers value, quality (integer), timestamp, and MXSTATUS_PROXY array.
|
||||||
|
- Item address format: `tag_name.AttributeName` for scalars, `tag_name.AttributeName[]` for whole arrays.
|
||||||
|
- If AddItem fails (e.g., tag does not exist), log Warning and return failure to caller.
|
||||||
|
- Bidirectional maps of `address ↔ itemHandle` are maintained for callback resolution.
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Use `AdviseSupervisory` (not `Advise`) because this is a background service with no interactive user session. AdviseSupervisory allows secured/verified writes without user authentication.
|
||||||
|
- Stored subscriptions dictionary maps address to callback for reconnect replay.
|
||||||
|
- On reconnect, all entries in stored subscriptions are re-subscribed (AddItem + AdviseSupervisory with new handles).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MXA-004: Tag Read/Write
|
||||||
|
|
||||||
|
The client shall support synchronous-style read and write operations, marshalled to the STA thread, with configurable timeouts.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Read: implemented as subscribe-get-first-value-unsubscribe pattern (AddItem → AdviseSupervisory → wait for OnDataChange → UnAdvise → RemoveItem).
|
||||||
|
- Write: AddItem → AdviseSupervisory → `Write()` → await `OnWriteComplete` callback → cleanup.
|
||||||
|
- Read timeout: configurable via `MxAccess:ReadTimeoutSeconds`, default 5 seconds.
|
||||||
|
- Write timeout: configurable via `MxAccess:WriteTimeoutSeconds`, default 5 seconds. On timeout, log Warning and return timeout error.
|
||||||
|
- Concurrent operation limit: configurable semaphore via `MxAccess:MaxConcurrentOperations`, default 10.
|
||||||
|
- All operations marshalled to the STA thread.
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Write uses security classification -1 (no security). Galaxy runtime handles security enforcement.
|
||||||
|
- `OnWriteComplete` callback: check MXSTATUS_PROXY `success` field. If 0, extract detail code and propagate error.
|
||||||
|
- COM exceptions (`COMException` with HRESULT) are caught and translated to meaningful error messages.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MXA-005: Auto-Reconnect
|
||||||
|
|
||||||
|
The client shall monitor connection health and automatically reconnect on failure, replaying all stored subscriptions after reconnect.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Monitor loop runs on a background thread, checking connection health at configurable interval (`MxAccess:MonitorIntervalSeconds`, default 5 seconds).
|
||||||
|
- If disconnected, attempt reconnect. On success, replay all stored subscriptions.
|
||||||
|
- On reconnect failure, log Warning and retry at next interval (no exponential backoff — reconnect as quickly as possible on a plant-floor service).
|
||||||
|
- Reconnect count is incremented on each successful reconnect.
|
||||||
|
- Monitor loop is cancellable (for clean shutdown).
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Reconnect cleans up old COM objects before creating new ones.
|
||||||
|
- After reconnect, probe subscription is re-established first, then stored subscriptions.
|
||||||
|
- No max retry limit — keep trying indefinitely until service is stopped.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MXA-006: Probe-Based Health Monitoring
|
||||||
|
|
||||||
|
The client shall optionally subscribe to a configurable probe tag and use OnDataChange callback staleness to detect silent connection failures.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Subscribe to a configurable probe tag (a known-good Galaxy attribute that changes periodically).
|
||||||
|
- Track `_lastProbeValueTime` (UTC) updated on each OnDataChange for the probe tag.
|
||||||
|
- If `DateTime.UtcNow - _lastProbeValueTime > staleThreshold`, force disconnect and reconnect.
|
||||||
|
- Probe tag address: configurable via `MxAccess:ProbeTag`. If not configured, probe monitoring is disabled.
|
||||||
|
- Stale threshold: configurable via `MxAccess:ProbeStaleThresholdSeconds`, default 60 seconds.
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- The probe tag should be an attribute that the Galaxy runtime updates regularly (e.g., a platform heartbeat or area-level timestamp). The specific tag is site-dependent.
|
||||||
|
- After forced reconnect, reset `_lastProbeValueTime` to `DateTime.UtcNow` to give the new connection a full threshold window.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MXA-007: COM Cleanup
|
||||||
|
|
||||||
|
On disconnect or disposal, the client shall unwire event handlers, unadvise/remove all items, unregister, and release COM objects via Marshal.ReleaseComObject.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Cleanup order: UnAdvise all active subscriptions → RemoveItem all items → unwire OnDataChange and OnWriteComplete event handlers → Unregister → `Marshal.ReleaseComObject`.
|
||||||
|
- On dispose: run disconnect if still connected, then dispose STA thread.
|
||||||
|
- Each cleanup step is wrapped in try/catch (cleanup must not throw).
|
||||||
|
- After cleanup: handle maps are cleared, pending write TCS entries are abandoned, COM reference is set to null.
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- `_storedSubscriptions` is NOT cleared on disconnect (preserved for reconnect replay). Only cleared on Dispose.
|
||||||
|
- Event handlers must be unwired BEFORE Unregister, or callbacks may fire on a dead object.
|
||||||
|
- `Marshal.ReleaseComObject` in a finally block, always, even if earlier steps fail.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MXA-008: Operation Metrics
|
||||||
|
|
||||||
|
The MXAccess client shall record timing and success/failure for Read, Write, and Subscribe operations.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Each operation records: duration (ms), success/failure.
|
||||||
|
- Metrics are available for the status dashboard: count, success rate, avg/min/max/P95 latency.
|
||||||
|
- Uses a rolling 1000-entry buffer for percentile calculation.
|
||||||
|
- Metrics are exposed via a queryable interface consumed by the status report service.
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Uses an `ITimingScope` pattern: `using (var scope = metrics.BeginOperation("read")) { ... }` for automatic timing and success tracking.
|
||||||
|
- Metrics are periodically logged at Debug level for diagnostics.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MXA-009: Error Code Translation
|
||||||
|
|
||||||
|
The client shall translate known MXAccess error codes from MXSTATUS_PROXY.detail into human-readable messages for logging and OPC UA status propagation.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Error 1008 → "User lacks security permission"
|
||||||
|
- Error 1012 → "Secured write required (one signature)"
|
||||||
|
- Error 1013 → "Verified write required (two signatures)"
|
||||||
|
- Unknown error codes are logged with their numeric value.
|
||||||
|
- Translated messages are included in OPC UA StatusCode descriptions and log entries.
|
||||||
229
docs/reqs/OpcUaServerReqs.md
Normal file
229
docs/reqs/OpcUaServerReqs.md
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
# OPC UA Server — Component Requirements
|
||||||
|
|
||||||
|
Parent: [HLR-001](HighLevelReqs.md#hlr-001-opc-ua-server), [HLR-002](HighLevelReqs.md#hlr-002-galaxy-hierarchy-as-opc-ua-address-space), [HLR-004](HighLevelReqs.md#hlr-004-data-type-mapping)
|
||||||
|
|
||||||
|
## OPC-001: Server Endpoint
|
||||||
|
|
||||||
|
The OPC UA server shall listen on a configurable TCP port (default 4840) using the OPC Foundation .NET Standard stack.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Server starts and accepts TCP connections on the configured port.
|
||||||
|
- Port is read from `appsettings.json` under `OpcUa:Port`; defaults to 4840 if absent.
|
||||||
|
- Endpoint URL format: `opc.tcp://<hostname>:<port>/LmxOpcUa`.
|
||||||
|
- If the port is in use at startup, log an Error and fail to start (do not silently pick another port).
|
||||||
|
- Security policy: None (no certificate validation). This is an internal plant-floor service.
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Configurable items: port (default 4840), endpoint path (default `/LmxOpcUa`), server application name (default `LmxOpcUa`).
|
||||||
|
- Server shall use the `OPCFoundation.NetStandard.Opc.Ua.Server` NuGet package.
|
||||||
|
- On startup, log the endpoint URL at Information level.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## OPC-002: Address Space Structure
|
||||||
|
|
||||||
|
The server shall create folder nodes for areas and object nodes for automation objects, organized in the same parent-child hierarchy as the Galaxy.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- The root folder node has BrowseName `ZB` (hardcoded Galaxy name).
|
||||||
|
- Objects where `is_area = 1` are created as FolderType nodes (organizational).
|
||||||
|
- Objects where `is_area = 0` are created as BaseObjectType nodes.
|
||||||
|
- Parent-child relationships use Organizes references (for areas) and HasComponent references (for contained objects).
|
||||||
|
- A client browsing Root → Objects → ZB → DEV → TestArea → TestMachine_001 → DelmiaReceiver sees the same structure as `gr/layout.md`.
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- NodeIds use a string-based identifier scheme: `ns=1;s=<tag_name>` for object nodes, `ns=1;s=<tag_name>.<attribute_name>` for variable nodes.
|
||||||
|
- Infrastructure objects (AppEngines, Platforms) are included in the tree but may have no variable children.
|
||||||
|
- When `contained_name` is null or empty, fall back to `tag_name` as the BrowseName.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## OPC-003: Variable Nodes for Attributes
|
||||||
|
|
||||||
|
Each user-defined attribute on a deployed object shall be represented as an OPC UA variable node under its parent object node.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Each row from `attributes.sql` creates one variable node under the matching object node (matched by `gobject_id`).
|
||||||
|
- Variable node BrowseName and DisplayName are set to `attribute_name`.
|
||||||
|
- Variable node stores `full_tag_reference` as its runtime MXAccess address.
|
||||||
|
- Variable nodes have AccessLevel = CurrentRead | CurrentWrite (3) by default.
|
||||||
|
- Objects with no user-defined attributes still appear as object nodes with zero children.
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Security classification from the attributes query is noted but not enforced at the OPC UA level (Galaxy runtime handles security).
|
||||||
|
- Attributes whose names start with `_` are already filtered by the SQL query.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## OPC-004: Browse Name Translation
|
||||||
|
|
||||||
|
Browse names shall use contained names (human-readable, scoped to parent). The server shall internally translate browse paths to tag_name references for MXAccess operations.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- A variable node browsed as `ZB/DEV/TestArea/TestMachine_001/DelmiaReceiver/DownloadPath` correctly translates to MXAccess reference `DelmiaReceiver_001.DownloadPath`.
|
||||||
|
- Translation uses the `tag_name` stored on the parent object node, not the browse path.
|
||||||
|
- No runtime path parsing — the mapping is baked into each node at build time.
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Each variable node stores its `full_tag_reference` (e.g., `DelmiaReceiver_001.DownloadPath`) at address-space build time. Read/write operations use this stored reference directly.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## OPC-005: Data Type Mapping
|
||||||
|
|
||||||
|
Variable nodes shall use OPC UA data types mapped from Galaxy mx_data_type values per the mapping in `gr/data_type_mapping.md`.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Every `mx_data_type` value in the mapping table produces the correct OPC UA DataType NodeId on the variable node.
|
||||||
|
- Unknown/unmapped `mx_data_type` values default to String (i=12).
|
||||||
|
- ElapsedTime (type 7) maps to Double representing seconds.
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Full mapping table in `gr/data_type_mapping.md`.
|
||||||
|
- DateTime conversion: Galaxy may store local time; convert to UTC for OPC UA.
|
||||||
|
- LocalizedText (type 15): use empty locale string with the text value.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## OPC-006: Array Support
|
||||||
|
|
||||||
|
Attributes marked as arrays shall have ValueRank=1 and ArrayDimensions set to the attribute's array_dimension value.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- `is_array = 1` produces ValueRank = 1 (OneDimension) and ArrayDimensions = `[array_dimension]`.
|
||||||
|
- `is_array = 0` produces ValueRank = -1 (Scalar) and no ArrayDimensions.
|
||||||
|
- MXAccess reference for array attributes uses `tag_name.attribute[]` (whole array) format.
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Individual array element access (`tag_name.attribute[n]`) is not required for initial implementation. Whole-array read/write only.
|
||||||
|
- If `array_dimension` is null or 0 when `is_array = 1`, log a Warning and default to ArrayDimensions = [0] (variable-length).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## OPC-007: Read Operations
|
||||||
|
|
||||||
|
The server shall fulfill OPC UA Read requests by reading the corresponding tag value from MXAccess using the tag_name.AttributeName reference.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- OPC UA Read request for a variable node results in a read via MXAccess using the node's stored `full_tag_reference`.
|
||||||
|
- Returned value is converted from the COM variant to the OPC UA data type specified on the node.
|
||||||
|
- OPC UA StatusCode reflects MXAccess quality: Good maps to Good, Bad/Uncertain map appropriately.
|
||||||
|
- If MXAccess is not connected, return StatusCode = Bad_NotConnected.
|
||||||
|
- Read timeout: configurable, default 5 seconds. On timeout, return Bad_Timeout.
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Prefer cached subscription-delivered values over on-demand reads to reduce COM round-trips.
|
||||||
|
- If no subscription is active for the tag, perform an on-demand read (AddItem, AdviseSupervisory, wait for first OnDataChange, then UnAdvise/RemoveItem).
|
||||||
|
- Concurrency: semaphore-limited to configurable max (default 10) concurrent MXAccess operations.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## OPC-008: Write Operations
|
||||||
|
|
||||||
|
The server shall fulfill OPC UA Write requests by writing to the corresponding tag via MXAccess.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- OPC UA Write request results in an MXAccess `Write()` call with completion confirmed via `OnWriteComplete()` callback.
|
||||||
|
- Write timeout: configurable, default 5 seconds. On timeout, log Warning and return Bad_Timeout.
|
||||||
|
- MXSTATUS_PROXY with `success = 0` causes the OPC UA write to return Bad_InternalError with the detail message.
|
||||||
|
- MXAccess errors 1008 (no permission), 1012 (secured write), 1013 (verified write) return Bad_UserAccessDenied.
|
||||||
|
- Write to a non-existent tag returns Bad_NodeIdUnknown.
|
||||||
|
- The server shall attempt to convert the written value to the expected Galaxy data type before passing to Write().
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Write uses security classification -1 (no security). Galaxy runtime handles security enforcement.
|
||||||
|
- Write sequence: uses existing subscription handle if available, otherwise AddItem + AdviseSupervisory + Write + await OnWriteComplete + cleanup.
|
||||||
|
- Concurrent write limit: same semaphore as reads (configurable, default 10).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## OPC-009: Subscriptions
|
||||||
|
|
||||||
|
The server shall support OPC UA subscriptions by mapping them to MXAccess advisory subscriptions and forwarding data change notifications.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- OPC UA CreateMonitoredItems results in MXAccess `AdviseSupervisory()` subscriptions for the requested tags.
|
||||||
|
- Data changes from `OnDataChange` callback are forwarded as OPC UA notifications to all subscribed clients.
|
||||||
|
- Shared subscriptions: if two OPC UA clients subscribe to the same tag, only one MXAccess subscription exists (ref-counted).
|
||||||
|
- Last subscriber unsubscribing triggers UnAdvise/RemoveItem on the MXAccess side.
|
||||||
|
- After MXAccess reconnect, all active MXAccess subscriptions are re-established automatically.
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Publishing interval from the OPC UA subscription request is honored on the OPC UA side; MXAccess delivers changes as fast as it receives them.
|
||||||
|
- OPC UA quality mapping from MXAccess quality integers: 192+ = Good, 64-191 = Uncertain, 0-63 = Bad.
|
||||||
|
- OnDataChange with MXSTATUS_PROXY failure: deliver notification with Bad quality to subscribed clients.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## OPC-010: Address Space Rebuild
|
||||||
|
|
||||||
|
When a Galaxy deployment change is detected, the server shall rebuild the address space without dropping existing OPC UA client connections where possible.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- When Galaxy Repository detects a deployment change, the OPC UA address space is rebuilt.
|
||||||
|
- Existing OPC UA client sessions are preserved — clients stay connected.
|
||||||
|
- Subscriptions for tags that still exist after rebuild continue to work.
|
||||||
|
- Subscriptions for tags that no longer exist receive a Bad_NodeIdUnknown status notification.
|
||||||
|
- Rebuild is logged at Information level with timing (duration).
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Rebuild is a full replace, not an incremental diff. Re-query hierarchy and attributes, build new tree, swap atomically.
|
||||||
|
- During rebuild, reads/writes against the old address space may fail briefly. This is acceptable.
|
||||||
|
- New MXAccess subscriptions for new tags are established; removed tags are unsubscribed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## OPC-011: Server Diagnostics Node
|
||||||
|
|
||||||
|
The server shall expose a ServerStatus node under the standard OPC UA Server object with ServerState, CurrentTime, and StartTime. This is required by the OPC UA specification for compliant servers.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- ServerState reports Running during normal operation.
|
||||||
|
- CurrentTime returns the server's current UTC time.
|
||||||
|
- StartTime returns the UTC time when the service started.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## OPC-012: Namespace Configuration
|
||||||
|
|
||||||
|
The server shall register a namespace URI at namespace index 1. All application-specific NodeIds shall use this namespace.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Namespace URI: `urn:ZB:LmxOpcUa` (Galaxy name is configurable).
|
||||||
|
- All object and variable NodeIds created from Galaxy data use namespace index 1.
|
||||||
|
- Standard OPC UA nodes remain in namespace 0.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## OPC-013: Session Management
|
||||||
|
|
||||||
|
The server shall support multiple concurrent OPC UA client sessions.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Maximum concurrent sessions: configurable, default 100.
|
||||||
|
- Session timeout: configurable, default 30 minutes of inactivity.
|
||||||
|
- Expired sessions are cleaned up and their subscriptions removed.
|
||||||
|
- Session count is reported to the status dashboard.
|
||||||
117
docs/reqs/ServiceHostReqs.md
Normal file
117
docs/reqs/ServiceHostReqs.md
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
# Service Host — Component Requirements
|
||||||
|
|
||||||
|
Parent: [HLR-006](HighLevelReqs.md#hlr-006-windows-service-hosting), [HLR-007](HighLevelReqs.md#hlr-007-logging)
|
||||||
|
|
||||||
|
## SVC-001: TopShelf Hosting
|
||||||
|
|
||||||
|
The application shall use TopShelf for Windows service lifecycle (install, uninstall, start, stop) and interactive console mode for development.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- TopShelf HostFactory configures the service with name `LmxOpcUa`, display name `LMX OPC UA Server`.
|
||||||
|
- Service installs via command line: `ZB.MOM.WW.LmxOpcUa.Host.exe install`.
|
||||||
|
- Service uninstalls via: `ZB.MOM.WW.LmxOpcUa.Host.exe uninstall`.
|
||||||
|
- Service runs as LocalSystem account (needed for MXAccess COM access and Windows Auth to SQL Server).
|
||||||
|
- Interactive console mode (exe with no args) works for development/debugging.
|
||||||
|
- `StartAutomatically` is set for Windows service registration.
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Platform target: x86 (32-bit) — required for MXAccess COM interop.
|
||||||
|
- Service description: "OPC UA server exposing System Platform Galaxy tags via MXAccess."
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SVC-002: Serilog Logging
|
||||||
|
|
||||||
|
The application shall configure Serilog with a rolling daily file sink and console sink, with log files retained for a configurable number of days (default 31).
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Console sink active (for interactive/debug mode).
|
||||||
|
- Rolling daily file sink writing to `logs/lmxopcua-YYYYMMDD.log`.
|
||||||
|
- Retained file count: configurable, default 31 days.
|
||||||
|
- Minimum log level: configurable, default Information.
|
||||||
|
- Log file path: configurable, default `logs/lmxopcua-.log`.
|
||||||
|
- Serilog is initialized before any other component (first thing in Main).
|
||||||
|
- `Log.CloseAndFlush()` called in finally block on exit.
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Structured logging with Serilog message templates (not string.Format).
|
||||||
|
- Log output includes timestamp, level, source context, message, and exception.
|
||||||
|
- Fatal exceptions are caught at the top level and logged before exit.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SVC-003: Configuration
|
||||||
|
|
||||||
|
The application shall load configuration from appsettings.json with support for environment-specific overrides (appsettings.*.json) and environment variables.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- `appsettings.json` is the primary configuration file.
|
||||||
|
- Environment-specific overrides via `appsettings.{environment}.json`.
|
||||||
|
- Configuration sections: `OpcUa`, `MxAccess`, `GalaxyRepository`, `Dashboard`.
|
||||||
|
- Missing optional configuration keys use documented defaults (service does not crash).
|
||||||
|
- Invalid configuration (e.g., port = -1) is detected at startup with a clear error message.
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Config is loaded once at startup. No hot-reload (service restart required for config changes). This is appropriate for an industrial service.
|
||||||
|
- All configurable values and their defaults are documented in `appsettings.json`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SVC-004: Graceful Shutdown
|
||||||
|
|
||||||
|
On service stop, the application shall gracefully shut down all components and flush logs before exiting.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- TopShelf WhenStopped triggers orderly shutdown.
|
||||||
|
- Shutdown sequence: (1) stop change detection polling, (2) stop OPC UA server (stop accepting new sessions, complete pending operations), (3) disconnect MXAccess (cleanup all COM objects), (4) stop status dashboard HTTP listener, (5) flush Serilog.
|
||||||
|
- Shutdown completes within 30 seconds (Windows SCM timeout).
|
||||||
|
- All IDisposable components are disposed in reverse-creation order.
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- `CancellationTokenSource` signals all background loops (monitor, change detection, HTTP listener) to stop.
|
||||||
|
- Log "Service shutdown complete" at Information level as the final log entry before flush.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SVC-005: Startup Sequence
|
||||||
|
|
||||||
|
The service shall start components in a defined order, with failure handling at each step.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Startup sequence:
|
||||||
|
1. Load configuration
|
||||||
|
2. Initialize Serilog
|
||||||
|
3. Start STA thread
|
||||||
|
4. Connect to MXAccess
|
||||||
|
5. Query Galaxy Repository for initial build
|
||||||
|
6. Build OPC UA address space
|
||||||
|
7. Start OPC UA server listener
|
||||||
|
8. Start change detection polling
|
||||||
|
9. Start status dashboard HTTP listener
|
||||||
|
- Failure in steps 1-4 prevents startup (service fails to start).
|
||||||
|
- Failure in steps 5-9 logs Error but allows the service to run in degraded mode.
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Degraded mode means the service is running but may have an empty address space (waiting for Galaxy DB) or no dashboard (port conflict). MXAccess connection is the minimum required for the service to be useful.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SVC-006: Unhandled Exception Handling
|
||||||
|
|
||||||
|
The service shall handle unexpected crashes gracefully.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Register `AppDomain.CurrentDomain.UnhandledException` handler that logs Fatal before the process terminates.
|
||||||
|
- TopShelf service recovery is configured: restart on failure with 60-second delay.
|
||||||
|
- Fatal-level log entry includes the full exception details.
|
||||||
157
docs/reqs/StatusDashboardReqs.md
Normal file
157
docs/reqs/StatusDashboardReqs.md
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
# Status Dashboard — Component Requirements
|
||||||
|
|
||||||
|
Parent: [HLR-009](HighLevelReqs.md#hlr-009-status-dashboard)
|
||||||
|
|
||||||
|
Reference: LmxProxy Status Dashboard (see `dashboard.JPG` in project root).
|
||||||
|
|
||||||
|
## DASH-001: Embedded HTTP Endpoint
|
||||||
|
|
||||||
|
The service shall host a lightweight HTTP listener on a configurable port serving a self-contained HTML status dashboard page (no external dependencies).
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Uses `System.Net.HttpListener` on a configurable port (`Dashboard:Port`, default 8081).
|
||||||
|
- Routes:
|
||||||
|
- `GET /` → HTML dashboard
|
||||||
|
- `GET /api/status` → JSON status report
|
||||||
|
- `GET /api/health` → 200 OK if healthy, 503 if unhealthy
|
||||||
|
- Only GET requests accepted; other methods return 405.
|
||||||
|
- Unknown paths return 404.
|
||||||
|
- All responses include `Cache-Control: no-cache, no-store, must-revalidate` headers.
|
||||||
|
- Dashboard can be disabled via config (`Dashboard:Enabled`, default true).
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- HTTP prefix: `http://+:{port}/` to bind to all interfaces.
|
||||||
|
- If HttpListener fails to start (port conflict, missing URL reservation), log Error and continue service startup without the dashboard.
|
||||||
|
- HTML page is self-contained: inline CSS, no external resources (no CDN, no JavaScript frameworks).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DASH-002: Connection Panel
|
||||||
|
|
||||||
|
The dashboard shall display a Connection panel showing MXAccess connection state.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Shows: **Connected** (True/False), **State** (Connected/Disconnected/Reconnecting/Error), **Connected Since** (UTC timestamp).
|
||||||
|
- Green left border when Connected, red when Disconnected/Error, yellow when Reconnecting.
|
||||||
|
- "Connected Since" shows "N/A" when not connected.
|
||||||
|
- Data sourced from MXAccess client's connection state properties.
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Timestamp format: `yyyy-MM-dd HH:mm:ss UTC`.
|
||||||
|
- Panel title: "Connection".
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DASH-003: Health Panel
|
||||||
|
|
||||||
|
The dashboard shall display a Health panel showing overall service health.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Three states: **Healthy** (green text), **Degraded** (yellow text), **Unhealthy** (red text).
|
||||||
|
- Includes a health message string explaining the status.
|
||||||
|
- Health rules:
|
||||||
|
- Not connected to MXAccess → Unhealthy
|
||||||
|
- Success rate < 50% with > 100 total operations → Degraded
|
||||||
|
- Connected with acceptable success rate → Healthy
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Health message examples: "LmxOpcUa is healthy", "MXAccess client is not connected", "Average success rate is below 50%".
|
||||||
|
- Green left border for Healthy, yellow for Degraded, red for Unhealthy.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DASH-004: Subscriptions Panel
|
||||||
|
|
||||||
|
The dashboard shall display a Subscriptions panel showing subscription statistics.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Shows: **Clients** (connected OPC UA client count), **Tags** (total variable nodes in address space), **Active** (active MXAccess subscriptions), **Delivered** (cumulative data change notifications delivered).
|
||||||
|
- Values update on each dashboard refresh.
|
||||||
|
- Zero values shown as "0", not blank.
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- "Tags" is the count of variable nodes, not object/folder nodes.
|
||||||
|
- "Active" is the count of distinct MXAccess item subscriptions (after ref-counting — the number of actual AdviseSupervisory calls, not the number of OPC UA monitored items).
|
||||||
|
- "Delivered" is a running counter since service start (not reset on reconnect).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DASH-005: Operations Table
|
||||||
|
|
||||||
|
The dashboard shall display an operations metrics table showing performance statistics.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Table with columns: **Operation**, **Count**, **Success Rate**, **Avg (ms)**, **Min (ms)**, **Max (ms)**, **P95 (ms)**.
|
||||||
|
- Rows: Read, Write, Subscribe, Browse.
|
||||||
|
- Empty cells show em-dash ("—") when no data available (count = 0).
|
||||||
|
- Success rate displayed as percentage (e.g., "99.8%").
|
||||||
|
- Latency values rounded to 1 decimal place.
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- Metrics sourced from the PerformanceMetrics component (1000-entry rolling buffer for percentile calculation).
|
||||||
|
- "Browse" row tracks OPC UA browse operations.
|
||||||
|
- "Subscribe" row tracks OPC UA CreateMonitoredItems operations.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DASH-006: Footer
|
||||||
|
|
||||||
|
The dashboard shall display a footer with last-updated time and service identification.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Format: "Last updated: {timestamp} UTC | Service: ZB.MOM.WW.LmxOpcUa.Host v{version}".
|
||||||
|
- Timestamp is the server-side UTC time when the HTML was generated.
|
||||||
|
- Version is read from the assembly version (`Assembly.GetExecutingAssembly().GetName().Version`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DASH-007: Auto-Refresh
|
||||||
|
|
||||||
|
The dashboard page shall auto-refresh to show current status without manual reload.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- HTML page includes `<meta http-equiv="refresh" content="10">` for 10-second auto-refresh.
|
||||||
|
- No JavaScript required for refresh (pure HTML meta-refresh).
|
||||||
|
- Refresh interval: configurable via `Dashboard:RefreshIntervalSeconds`, default 10 seconds.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DASH-008: JSON Status API
|
||||||
|
|
||||||
|
The `/api/status` endpoint shall return a JSON object with all dashboard data for programmatic consumption.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Response Content-Type: `application/json`.
|
||||||
|
- JSON structure includes: connection state, health status, subscription statistics, and operation metrics.
|
||||||
|
- Same data as the HTML dashboard, structured for machine consumption.
|
||||||
|
- Suitable for integration with external monitoring tools.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DASH-009: Galaxy Info Panel
|
||||||
|
|
||||||
|
The dashboard shall display a Galaxy Info panel showing Galaxy Repository state.
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
|
||||||
|
- Shows: **Galaxy Name** (e.g., ZB), **DB Status** (Connected/Disconnected), **Last Deploy** (timestamp from `galaxy.time_of_last_deploy`), **Objects** (count), **Attributes** (count), **Last Rebuild** (timestamp of last address space rebuild).
|
||||||
|
- Provides visibility into the Galaxy Repository component's state independently of MXAccess connection status.
|
||||||
|
|
||||||
|
### Details
|
||||||
|
|
||||||
|
- "DB Status" reflects whether the most recent change detection poll succeeded.
|
||||||
|
- "Last Deploy" shows the raw `time_of_last_deploy` value from the Galaxy database.
|
||||||
|
- "Objects" and "Attributes" show counts from the most recent successful hierarchy/attribute query.
|
||||||
51
gr/CLAUDE.md
Normal file
51
gr/CLAUDE.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
The goal of this project is to identify and develop SQL queries that extract the Galaxy object hierarchy from the **System Platform Galaxy Repository** database in order to build a tag structure for an OPC UA server.
|
||||||
|
|
||||||
|
Specifically, we need to:
|
||||||
|
- Build the hierarchy of **areas** and **automation objects** (using contained names for human-readable browsing)
|
||||||
|
- Translate contained names to **tag_names** for read/write operations (e.g., `TestMachine_001.DelmiaReceiver` in the hierarchy becomes `DelmiaReceiver_001` when addressing tag values)
|
||||||
|
|
||||||
|
See `layout.md` for details on the hierarchy vs tag name relationship.
|
||||||
|
|
||||||
|
## Key Files
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
- `connectioninfo.md` — Database connection details and sqlcmd usage
|
||||||
|
- `layout.md` — Galaxy object hierarchy, contained_name vs tag_name translation, and target OPC UA structure
|
||||||
|
- `build_layout_plan.md` — Step-by-step plan for extracting hierarchy, attaching attributes, and monitoring for changes
|
||||||
|
- `data_type_mapping.md` — Galaxy mx_data_type to OPC UA DataType mapping, including array handling (ValueRank, ArrayDimensions)
|
||||||
|
|
||||||
|
### Queries
|
||||||
|
- `queries/hierarchy.sql` — Deployed object hierarchy with browse names and parent relationships
|
||||||
|
- `queries/attributes.sql` — User-defined (dynamic) attributes with data types and array dimensions
|
||||||
|
- `queries/attributes_extended.sql` — All attributes (system + user-defined) with data types and array dimensions
|
||||||
|
- `queries/change_detection.sql` — Poll `galaxy.time_of_last_deploy` to detect deployment changes
|
||||||
|
|
||||||
|
### Schema Reference
|
||||||
|
- `schema.md` — Full schema reference for all tables and views in the ZB database
|
||||||
|
- `ddl/tables/` — Individual CREATE TABLE definitions
|
||||||
|
- `ddl/views/` — Individual view definitions
|
||||||
|
|
||||||
|
## Working with the Galaxy Repository Database
|
||||||
|
|
||||||
|
The Galaxy Repository is the backing SQL Server database for Wonderware/AVEVA System Platform (Galaxy: ZB, localhost, Windows Auth). Key tables used by the queries:
|
||||||
|
|
||||||
|
- **gobject** — Object instances, hierarchy (contained_by_gobject_id, area_gobject_id), deployment state (deployed_package_id)
|
||||||
|
- **template_definition** — Object type categories (category_id distinguishes areas, engines, user-defined objects, etc.)
|
||||||
|
- **dynamic_attribute** — User-defined attributes on templates, inherited by instances via derived_from_gobject_id chain
|
||||||
|
- **attribute_definition** — System/primitive attributes
|
||||||
|
- **primitive_instance** — Links objects to their primitive components and attribute definitions
|
||||||
|
- **galaxy** — Single-row table with time_of_last_deploy for change detection
|
||||||
|
|
||||||
|
Use `sqlcmd -S localhost -d ZB -E -Q "..."` to run queries. See `connectioninfo.md` for details.
|
||||||
|
|
||||||
|
## Conventions
|
||||||
|
|
||||||
|
- Store all connection parameters in `connectioninfo.md`, not scattered across scripts.
|
||||||
|
- Keep SQL query examples and extraction notes as Markdown files in this repo.
|
||||||
|
- If scripts are added (Python, PowerShell, etc.), document their usage and dependencies alongside them.
|
||||||
84
gr/build_layout_plan.md
Normal file
84
gr/build_layout_plan.md
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
# OPC UA Server Layout — Build Plan
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Extract the Galaxy object hierarchy and tag definitions from the ZB (Galaxy Repository) database to construct an OPC UA server address space. The root node is hardcoded as **ZB**.
|
||||||
|
|
||||||
|
## Step 1: Build the Browse Tree
|
||||||
|
|
||||||
|
Run `queries/hierarchy.sql` to get all deployed automation objects and their parent-child relationships.
|
||||||
|
|
||||||
|
For each row returned:
|
||||||
|
- `parent_gobject_id = 0` → child of the root ZB node
|
||||||
|
- `is_area = 1` → create as an OPC UA folder node (organizational)
|
||||||
|
- `is_area = 0` → create as an OPC UA object node (container for tags)
|
||||||
|
- Use `browse_name` as the OPC UA BrowseName/DisplayName
|
||||||
|
- Store `gobject_id` and `tag_name` for attribute lookup and tag reference translation
|
||||||
|
|
||||||
|
Build the tree by matching each row's `parent_gobject_id` to another row's `gobject_id`. The result is:
|
||||||
|
|
||||||
|
```
|
||||||
|
ZB (root, hardcoded)
|
||||||
|
└── DEV (folder, is_area=1)
|
||||||
|
├── DevAppEngine (object)
|
||||||
|
├── DevPlatform (object)
|
||||||
|
└── TestArea (folder, is_area=1)
|
||||||
|
├── DevTestObject (object)
|
||||||
|
└── TestMachine_001 (object)
|
||||||
|
├── DelmiaReceiver (object, browse_name from contained_name)
|
||||||
|
└── MESReceiver (object, browse_name from contained_name)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 2: Attach Attributes as Tag Nodes
|
||||||
|
|
||||||
|
Run `queries/attributes.sql` to get all user-defined attributes for deployed objects.
|
||||||
|
|
||||||
|
For each attribute row:
|
||||||
|
- Match to the browse tree via `gobject_id`
|
||||||
|
- Create an OPC UA variable node under the matching object node
|
||||||
|
- Use `attribute_name` as the BrowseName/DisplayName
|
||||||
|
- Use `full_tag_reference` as the runtime tag path for read/write operations
|
||||||
|
- Map `mx_data_type` to OPC UA built-in types:
|
||||||
|
|
||||||
|
| mx_data_type | Description | OPC UA Type |
|
||||||
|
|--------------|-------------|-------------|
|
||||||
|
| 1 | Boolean | Boolean |
|
||||||
|
| 2 | Integer | Int32 |
|
||||||
|
| 3 | Float | Float |
|
||||||
|
| 4 | Double | Double |
|
||||||
|
| 5 | String | String |
|
||||||
|
| 6 | Time | DateTime |
|
||||||
|
| 7 | ElapsedTime | Double (seconds) or Duration |
|
||||||
|
|
||||||
|
- If `is_array = 1`, create the variable as an array with rank 1 and dimension from `array_dimension`
|
||||||
|
|
||||||
|
## Step 3: Monitor for Changes
|
||||||
|
|
||||||
|
Poll `queries/change_detection.sql` on a regular interval (e.g., every 30 seconds).
|
||||||
|
|
||||||
|
```
|
||||||
|
SELECT time_of_last_deploy FROM galaxy;
|
||||||
|
```
|
||||||
|
|
||||||
|
Compare the returned `time_of_last_deploy` to the last known value:
|
||||||
|
- **No change** → do nothing
|
||||||
|
- **Changed** → a deployment occurred; re-run Steps 1 and 2 to rebuild the address space
|
||||||
|
|
||||||
|
This handles objects being deployed, undeployed, added, or removed.
|
||||||
|
|
||||||
|
## Connection Details
|
||||||
|
|
||||||
|
See `connectioninfo.md` for database connection parameters and sqlcmd usage.
|
||||||
|
|
||||||
|
```
|
||||||
|
sqlcmd -S localhost -d ZB -E -Q "YOUR QUERY HERE"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Query Files
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `queries/hierarchy.sql` | Deployed object hierarchy with browse names and parent relationships |
|
||||||
|
| `queries/attributes.sql` | User-defined attributes with data types and array dimensions |
|
||||||
|
| `queries/attributes_extended.sql` | All attributes (system + user-defined) with data types and array dimensions |
|
||||||
|
| `queries/change_detection.sql` | Poll galaxy.time_of_last_deploy for deployment changes |
|
||||||
26
gr/connectioninfo.md
Normal file
26
gr/connectioninfo.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Galaxy Repository — Connection Information
|
||||||
|
|
||||||
|
## Database Connection
|
||||||
|
|
||||||
|
| Parameter | Value |
|
||||||
|
|-----------------|----------------|
|
||||||
|
| Server | localhost (default instance) |
|
||||||
|
| Database Name | ZB |
|
||||||
|
| Port | 1433 (default) |
|
||||||
|
| Authentication | Windows Auth |
|
||||||
|
| Username | dohertj2 |
|
||||||
|
|
||||||
|
## sqlcmd Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
sqlcmd -S localhost -d ZB -E -Q "YOUR QUERY HERE"
|
||||||
|
```
|
||||||
|
|
||||||
|
- `-S localhost` — default instance
|
||||||
|
- `-d ZB` — database name
|
||||||
|
- `-E` — Windows Authentication (dohertj2)
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- The Galaxy Repository is a SQL Server database created and managed by AVEVA System Platform (formerly Wonderware).
|
||||||
|
- Typically accessed via SQL Server Management Studio (SSMS), `sqlcmd`, or programmatically via ODBC/ADO.NET/pyodbc.
|
||||||
80
gr/data_type_mapping.md
Normal file
80
gr/data_type_mapping.md
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# Data Type Mapping — Galaxy Repository to OPC UA
|
||||||
|
|
||||||
|
## Scalar Type Mapping
|
||||||
|
|
||||||
|
| mx_data_type | Galaxy Description | OPC UA DataType | OPC UA NodeId | Notes |
|
||||||
|
|--------------|--------------------|-----------------|---------------|-------|
|
||||||
|
| 1 | Boolean | Boolean | i=1 | Direct mapping |
|
||||||
|
| 2 | Integer (Int32) | Int32 | i=6 | Galaxy integers are 32-bit signed |
|
||||||
|
| 3 | Float (Single) | Float | i=10 | 32-bit IEEE 754 |
|
||||||
|
| 4 | Double | Double | i=11 | 64-bit IEEE 754 |
|
||||||
|
| 5 | String | String | i=12 | Unicode string |
|
||||||
|
| 6 | Time (DateTime) | DateTime | i=13 | Galaxy DateTime to OPC UA DateTime (100ns ticks since 1601-01-01) |
|
||||||
|
| 7 | ElapsedTime (TimeSpan) | Double | i=11 | No native OPC UA TimeSpan; map to Double representing seconds (or use Duration type alias, NodeId i=290) |
|
||||||
|
| 8 | (reference) | String | i=12 | Object reference; expose as string representation |
|
||||||
|
| 13 | (enumeration) | Int32 | i=6 | Enum backing value is integer |
|
||||||
|
| 14 | (custom) | String | i=12 | Fallback to string |
|
||||||
|
| 15 | InternationalizedString | LocalizedText | i=21 | OPC UA LocalizedText supports locale + text pairs |
|
||||||
|
| 16 | (custom) | String | i=12 | Fallback to string |
|
||||||
|
|
||||||
|
## OPC UA Built-in Type Reference
|
||||||
|
|
||||||
|
For context, the full set of OPC UA built-in types and their NodeIds:
|
||||||
|
|
||||||
|
| NodeId | Type | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| i=1 | Boolean | True/false |
|
||||||
|
| i=2 | SByte | Signed 8-bit integer |
|
||||||
|
| i=3 | Byte | Unsigned 8-bit integer |
|
||||||
|
| i=4 | Int16 | Signed 16-bit integer |
|
||||||
|
| i=5 | UInt16 | Unsigned 16-bit integer |
|
||||||
|
| i=6 | Int32 | Signed 32-bit integer |
|
||||||
|
| i=7 | UInt32 | Unsigned 32-bit integer |
|
||||||
|
| i=8 | Int64 | Signed 64-bit integer |
|
||||||
|
| i=9 | UInt64 | Unsigned 64-bit integer |
|
||||||
|
| i=10 | Float | 32-bit IEEE 754 |
|
||||||
|
| i=11 | Double | 64-bit IEEE 754 |
|
||||||
|
| i=12 | String | Unicode string |
|
||||||
|
| i=13 | DateTime | Date and time (100ns ticks since 1601-01-01) |
|
||||||
|
| i=14 | Guid | 128-bit globally unique identifier |
|
||||||
|
| i=15 | ByteString | Sequence of bytes |
|
||||||
|
| i=21 | LocalizedText | Locale + text pair |
|
||||||
|
|
||||||
|
## Array Handling
|
||||||
|
|
||||||
|
When `is_array = 1` in the attributes query, the OPC UA variable node must be configured as an array.
|
||||||
|
|
||||||
|
### ValueRank
|
||||||
|
|
||||||
|
Set on the OPC UA variable node to indicate scalar vs array:
|
||||||
|
|
||||||
|
| is_array | ValueRank | Meaning |
|
||||||
|
|----------|-----------|---------|
|
||||||
|
| 0 | -1 (Scalar) | Value is not an array |
|
||||||
|
| 1 | 1 (OneDimension) | Value is a one-dimensional array |
|
||||||
|
|
||||||
|
### ArrayDimensions
|
||||||
|
|
||||||
|
When `ValueRank = 1`, set the `ArrayDimensions` attribute to a single-element array containing the `array_dimension` value from the attributes query.
|
||||||
|
|
||||||
|
Example for `MESReceiver_001.MoveInPartNumbers` (`is_array=1`, `array_dimension=50`):
|
||||||
|
- DataType: String (i=12)
|
||||||
|
- ValueRank: 1
|
||||||
|
- ArrayDimensions: [50]
|
||||||
|
|
||||||
|
Example for `TestMachine_001.MachineID` (`is_array=0`):
|
||||||
|
- DataType: String (i=12)
|
||||||
|
- ValueRank: -1
|
||||||
|
- ArrayDimensions: (not set)
|
||||||
|
|
||||||
|
## DateTime Conversion
|
||||||
|
|
||||||
|
Galaxy `Time` (mx_data_type=6) stores DateTime values. OPC UA DateTime is defined as the number of 100-nanosecond intervals since January 1, 1601 (UTC). Ensure the conversion accounts for:
|
||||||
|
- Timezone: Galaxy may store local time; OPC UA expects UTC
|
||||||
|
- Epoch difference: adjust if Galaxy uses a different epoch (e.g., Unix epoch 1970-01-01)
|
||||||
|
|
||||||
|
## ElapsedTime Handling
|
||||||
|
|
||||||
|
Galaxy `ElapsedTime` (mx_data_type=7) represents a duration/timespan. OPC UA has no native TimeSpan type. Options:
|
||||||
|
- **Double (i=11)**: Store as seconds (recommended for simplicity)
|
||||||
|
- **Duration (i=290)**: OPC UA type alias for Double, semantically represents milliseconds — use if the OPC UA SDK supports it
|
||||||
13
gr/ddl/tables/ConversionQueue.sql
Normal file
13
gr/ddl/tables/ConversionQueue.sql
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
-- Table: ConversionQueue
|
||||||
|
CREATE TABLE [ConversionQueue] (
|
||||||
|
[id] int NULL,
|
||||||
|
[Name] nvarchar(329) NULL,
|
||||||
|
[IsCheckedOut] bit NOT NULL,
|
||||||
|
[Status] bit NOT NULL DEFAULT ((0)),
|
||||||
|
[MetaData] nchar(256) NULL,
|
||||||
|
[OperationType] nchar(20) NOT NULL,
|
||||||
|
[timestamp_of_last_change] bigint NULL,
|
||||||
|
[change_type] int NULL
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
9
gr/ddl/tables/CurrentSessionContainedName.sql
Normal file
9
gr/ddl/tables/CurrentSessionContainedName.sql
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
-- Table: CurrentSessionContainedName
|
||||||
|
CREATE TABLE [CurrentSessionContainedName] (
|
||||||
|
[Uniqeid] int NOT NULL,
|
||||||
|
[obj_id] int NULL,
|
||||||
|
[containedname] nvarchar(32) NULL,
|
||||||
|
CONSTRAINT [PK_CurrentSessionContainedName] PRIMARY KEY ([Uniqeid])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
7
gr/ddl/tables/ImportTransaction.sql
Normal file
7
gr/ddl/tables/ImportTransaction.sql
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
-- Table: ImportTransaction
|
||||||
|
CREATE TABLE [ImportTransaction] (
|
||||||
|
[ImportOperationId] nvarchar(329) NULL,
|
||||||
|
[Status] bit NOT NULL DEFAULT ((1))
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
8
gr/ddl/tables/aa_sql_objects.sql
Normal file
8
gr/ddl/tables/aa_sql_objects.sql
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
-- Table: aa_sql_objects
|
||||||
|
CREATE TABLE [aa_sql_objects] (
|
||||||
|
[object_name] nvarchar(128) NOT NULL,
|
||||||
|
[object_type] nvarchar(10) NOT NULL,
|
||||||
|
CONSTRAINT [PK_aa_sql_objects] PRIMARY KEY ([object_name])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
9
gr/ddl/tables/affected_overview_symbols.sql
Normal file
9
gr/ddl/tables/affected_overview_symbols.sql
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
-- Table: affected_overview_symbols
|
||||||
|
CREATE TABLE [affected_overview_symbols] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[package_id] int NOT NULL,
|
||||||
|
[mx_primitive_id] smallint NOT NULL,
|
||||||
|
[visual_element_id] int NOT NULL
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
8
gr/ddl/tables/alarm_message_defaults.sql
Normal file
8
gr/ddl/tables/alarm_message_defaults.sql
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
-- Table: alarm_message_defaults
|
||||||
|
CREATE TABLE [alarm_message_defaults] (
|
||||||
|
[phrase_id] int NOT NULL,
|
||||||
|
[default_message] nvarchar(1024) NOT NULL,
|
||||||
|
CONSTRAINT [PK_alarm_message_defaults] PRIMARY KEY ([phrase_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
8
gr/ddl/tables/alarm_message_timestamps.sql
Normal file
8
gr/ddl/tables/alarm_message_timestamps.sql
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
-- Table: alarm_message_timestamps
|
||||||
|
CREATE TABLE [alarm_message_timestamps] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[timestamp_of_populate] bigint NOT NULL DEFAULT ((0)),
|
||||||
|
CONSTRAINT [PK_alarm_message_timestamps] PRIMARY KEY ([gobject_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
12
gr/ddl/tables/alarm_message_translations.sql
Normal file
12
gr/ddl/tables/alarm_message_translations.sql
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
-- Table: alarm_message_translations
|
||||||
|
CREATE TABLE [alarm_message_translations] (
|
||||||
|
[phrase_id] int NOT NULL,
|
||||||
|
[locale_id] smallint NOT NULL,
|
||||||
|
[translated_message] nvarchar(1024) NOT NULL,
|
||||||
|
CONSTRAINT [PK_alarm_message_translations] PRIMARY KEY ([phrase_id], [locale_id], [phrase_id], [locale_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [alarm_message_translations] ADD FOREIGN KEY ([locale_id]) REFERENCES [supported_locales] ([locale_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
13
gr/ddl/tables/alarm_messages.sql
Normal file
13
gr/ddl/tables/alarm_messages.sql
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
-- Table: alarm_messages
|
||||||
|
CREATE TABLE [alarm_messages] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[package_id] int NOT NULL,
|
||||||
|
[mx_primitive_id] smallint NOT NULL,
|
||||||
|
[phrase_id] int NOT NULL,
|
||||||
|
CONSTRAINT [PK_alarm_messages] PRIMARY KEY ([gobject_id], [package_id], [mx_primitive_id], [phrase_id], [gobject_id], [gobject_id], [mx_primitive_id], [package_id], [gobject_id], [mx_primitive_id], [package_id], [gobject_id], [mx_primitive_id], [package_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [alarm_messages] ADD FOREIGN KEY ([package_id]) REFERENCES [primitive_instance] ([package_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
24
gr/ddl/tables/attribute_definition.sql
Normal file
24
gr/ddl/tables/attribute_definition.sql
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
-- Table: attribute_definition
|
||||||
|
CREATE TABLE [attribute_definition] (
|
||||||
|
[attribute_definition_id] int NOT NULL,
|
||||||
|
[primitive_definition_id] int NOT NULL,
|
||||||
|
[attribute_name] nvarchar(329) NOT NULL,
|
||||||
|
[mx_attribute_id] smallint NOT NULL,
|
||||||
|
[has_config_set_handler] bit NOT NULL,
|
||||||
|
[mx_data_type] smallint NOT NULL,
|
||||||
|
[is_array] bit NOT NULL,
|
||||||
|
[security_classification] smallint NOT NULL,
|
||||||
|
[security_classification_needs_deployed] bit NOT NULL,
|
||||||
|
[mx_attribute_category] int NOT NULL,
|
||||||
|
[is_frequently_accessed] bit NOT NULL,
|
||||||
|
[is_locked] bit NOT NULL,
|
||||||
|
[is_locked_needs_deployed] bit NOT NULL,
|
||||||
|
[mx_value] text(2147483647) NOT NULL,
|
||||||
|
[mx_value_needs_deployed] bit NOT NULL,
|
||||||
|
CONSTRAINT [PK_attribute_definition] PRIMARY KEY ([primitive_definition_id], [mx_attribute_id], [primitive_definition_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [attribute_definition] ADD FOREIGN KEY ([primitive_definition_id]) REFERENCES [primitive_definition] ([primitive_definition_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
26
gr/ddl/tables/attribute_reference.sql
Normal file
26
gr/ddl/tables/attribute_reference.sql
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
-- Table: attribute_reference
|
||||||
|
CREATE TABLE [attribute_reference] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[package_id] int NOT NULL,
|
||||||
|
[referring_mx_primitive_id] smallint NOT NULL DEFAULT ((0)),
|
||||||
|
[referring_mx_attribute_id] smallint NOT NULL DEFAULT ((0)),
|
||||||
|
[element_index] smallint NOT NULL DEFAULT ((0)),
|
||||||
|
[resolved_gobject_id] int NOT NULL DEFAULT ((0)),
|
||||||
|
[reference_string] nvarchar(700) NOT NULL DEFAULT (''),
|
||||||
|
[context_string] nvarchar(329) NOT NULL DEFAULT (''),
|
||||||
|
[object_signature] int NOT NULL DEFAULT ((0)),
|
||||||
|
[resolved_mx_primitive_id] smallint NOT NULL DEFAULT ((0)),
|
||||||
|
[resolved_mx_attribute_id] smallint NOT NULL DEFAULT ((0)),
|
||||||
|
[resolved_mx_property_id] smallint NOT NULL DEFAULT ((0)),
|
||||||
|
[attribute_signature] int NOT NULL DEFAULT ((0)),
|
||||||
|
[lock_type] int NOT NULL DEFAULT ((0)),
|
||||||
|
[is_valid] bit NOT NULL DEFAULT ((0)),
|
||||||
|
[attr_res_status] int NOT NULL DEFAULT ((0)),
|
||||||
|
[attribute_index] smallint NULL DEFAULT ((-1)),
|
||||||
|
CONSTRAINT [PK_attribute_reference] PRIMARY KEY ([gobject_id], [package_id], [referring_mx_primitive_id], [referring_mx_attribute_id], [element_index], [gobject_id], [package_id], [referring_mx_primitive_id], [gobject_id], [package_id], [referring_mx_primitive_id], [gobject_id], [package_id], [referring_mx_primitive_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [attribute_reference] ADD FOREIGN KEY ([referring_mx_primitive_id]) REFERENCES [primitive_instance] ([package_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
11
gr/ddl/tables/attributes_translation_table.sql
Normal file
11
gr/ddl/tables/attributes_translation_table.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
-- Table: attributes_translation_table
|
||||||
|
CREATE TABLE [attributes_translation_table] (
|
||||||
|
[gobject_id] int NULL,
|
||||||
|
[attribute_name] nvarchar(329) NOT NULL,
|
||||||
|
[new_primitive_id] int NULL,
|
||||||
|
[new_attribute_id] int NULL,
|
||||||
|
[old_primitive_id] int NULL,
|
||||||
|
[old_attribute_id] int NULL
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
11
gr/ddl/tables/autobind_device.sql
Normal file
11
gr/ddl/tables/autobind_device.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
-- Table: autobind_device
|
||||||
|
CREATE TABLE [autobind_device] (
|
||||||
|
[dio_id] int NOT NULL,
|
||||||
|
[overridden_naming_rule_id] int NULL,
|
||||||
|
CONSTRAINT [PK_autobind_device] PRIMARY KEY ([dio_id], [overridden_naming_rule_id], [dio_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [autobind_device] ADD FOREIGN KEY ([dio_id]) REFERENCES [gobject] ([gobject_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
11
gr/ddl/tables/autobind_device_category.sql
Normal file
11
gr/ddl/tables/autobind_device_category.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
-- Table: autobind_device_category
|
||||||
|
CREATE TABLE [autobind_device_category] (
|
||||||
|
[category_id] smallint NOT NULL,
|
||||||
|
[rule_id] int NULL DEFAULT ((0)),
|
||||||
|
CONSTRAINT [PK_autobind_device_category] PRIMARY KEY ([category_id], [rule_id], [category_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [autobind_device_category] ADD FOREIGN KEY ([category_id]) REFERENCES [lookup_category] ([category_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
11
gr/ddl/tables/autobind_device_template.sql
Normal file
11
gr/ddl/tables/autobind_device_template.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
-- Table: autobind_device_template
|
||||||
|
CREATE TABLE [autobind_device_template] (
|
||||||
|
[template_definition_id] int NOT NULL,
|
||||||
|
[rule_id] int NULL,
|
||||||
|
CONSTRAINT [PK_autobind_device_template] PRIMARY KEY ([template_definition_id], [rule_id], [template_definition_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [autobind_device_template] ADD FOREIGN KEY ([template_definition_id]) REFERENCES [template_definition] ([template_definition_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
13
gr/ddl/tables/autobind_device_topic.sql
Normal file
13
gr/ddl/tables/autobind_device_topic.sql
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
-- Table: autobind_device_topic
|
||||||
|
CREATE TABLE [autobind_device_topic] (
|
||||||
|
[dio_id] int NOT NULL,
|
||||||
|
[sg_mx_primitive_id] smallint NOT NULL DEFAULT ((0)),
|
||||||
|
[overridden_naming_rule_id] int NULL,
|
||||||
|
[default_xlate_rule_id] int NOT NULL DEFAULT ((0)),
|
||||||
|
CONSTRAINT [PK_autobind_device_topic] PRIMARY KEY ([dio_id], [sg_mx_primitive_id], [overridden_naming_rule_id], [dio_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [autobind_device_topic] ADD FOREIGN KEY ([dio_id]) REFERENCES [autobind_device] ([dio_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
8
gr/ddl/tables/autobind_naming_rule.sql
Normal file
8
gr/ddl/tables/autobind_naming_rule.sql
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
-- Table: autobind_naming_rule
|
||||||
|
CREATE TABLE [autobind_naming_rule] (
|
||||||
|
[rule_id] int NOT NULL,
|
||||||
|
[rule_name] nvarchar(329) NOT NULL,
|
||||||
|
CONSTRAINT [PK_autobind_naming_rule] PRIMARY KEY ([rule_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
12
gr/ddl/tables/autobind_naming_rule_spec.sql
Normal file
12
gr/ddl/tables/autobind_naming_rule_spec.sql
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
-- Table: autobind_naming_rule_spec
|
||||||
|
CREATE TABLE [autobind_naming_rule_spec] (
|
||||||
|
[rule_id] int NOT NULL,
|
||||||
|
[io_type] nchar(1) NOT NULL,
|
||||||
|
[rule_spec] nvarchar(512) NOT NULL,
|
||||||
|
CONSTRAINT [PK_autobind_naming_rule_spec] PRIMARY KEY ([rule_id], [io_type], [rule_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [autobind_naming_rule_spec] ADD FOREIGN KEY ([rule_id]) REFERENCES [autobind_naming_rule] ([rule_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
10
gr/ddl/tables/autobind_translation_rule.sql
Normal file
10
gr/ddl/tables/autobind_translation_rule.sql
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
-- Table: autobind_translation_rule
|
||||||
|
CREATE TABLE [autobind_translation_rule] (
|
||||||
|
[xlate_rule_id] int NOT NULL,
|
||||||
|
[xlate_rule_name] nvarchar(329) NOT NULL,
|
||||||
|
[xlate_rule_gsub_str] nvarchar(1000) NULL,
|
||||||
|
[xlate_rule_scope_global] bit NOT NULL DEFAULT ((0)),
|
||||||
|
CONSTRAINT [PK_autobind_translation_rule] PRIMARY KEY ([xlate_rule_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
17
gr/ddl/tables/autobound_attribute.sql
Normal file
17
gr/ddl/tables/autobound_attribute.sql
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
-- Table: autobound_attribute
|
||||||
|
CREATE TABLE [autobound_attribute] (
|
||||||
|
[dio_id] int NOT NULL,
|
||||||
|
[sg_mx_primitive_id] smallint NOT NULL DEFAULT ((0)),
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[mx_primitive_id] smallint NOT NULL,
|
||||||
|
[mx_attribute_id] smallint NOT NULL,
|
||||||
|
[element_index] smallint NOT NULL DEFAULT ((0)),
|
||||||
|
[attr_alias] nvarchar(329) NULL,
|
||||||
|
[xlate_rule_id] int NOT NULL DEFAULT ((0)),
|
||||||
|
CONSTRAINT [PK_autobound_attribute] PRIMARY KEY ([gobject_id], [mx_primitive_id], [mx_attribute_id], [element_index], [dio_id], [sg_mx_primitive_id], [dio_id], [sg_mx_primitive_id], [xlate_rule_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [autobound_attribute] ADD FOREIGN KEY ([xlate_rule_id]) REFERENCES [autobind_translation_rule] ([xlate_rule_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
9
gr/ddl/tables/client_control_class_link.sql
Normal file
9
gr/ddl/tables/client_control_class_link.sql
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
-- Table: client_control_class_link
|
||||||
|
CREATE TABLE [client_control_class_link] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[file_id] int NULL,
|
||||||
|
[class_name] nvarchar(1024) NOT NULL,
|
||||||
|
CONSTRAINT [PK_client_control_class_link] PRIMARY KEY ([gobject_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
11
gr/ddl/tables/client_info.sql
Normal file
11
gr/ddl/tables/client_info.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
-- Table: client_info
|
||||||
|
CREATE TABLE [client_info] (
|
||||||
|
[id] int NOT NULL,
|
||||||
|
[client_unique_identifier] nvarchar(4000) NOT NULL,
|
||||||
|
[client_name] nvarchar(64) NOT NULL,
|
||||||
|
[deployed_files_count] smallint NOT NULL,
|
||||||
|
[time_of_last_deployed_object_components] datetime NULL DEFAULT (getdate()),
|
||||||
|
[timestamp_of_last_synchronized] bigint NOT NULL DEFAULT ((0))
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
16
gr/ddl/tables/control_index.sql
Normal file
16
gr/ddl/tables/control_index.sql
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
-- Table: control_index
|
||||||
|
CREATE TABLE [control_index] (
|
||||||
|
[entity_id] int NOT NULL,
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[control_id] nvarchar(329) NULL,
|
||||||
|
[control_name] nvarchar(329) NOT NULL,
|
||||||
|
[control_description] nvarchar(2000) NULL,
|
||||||
|
[properties] nvarchar(-1) NULL,
|
||||||
|
[thumbnail] nvarchar(-1) NULL,
|
||||||
|
CONSTRAINT [PK_control_index] PRIMARY KEY ([gobject_id], [control_name], [gobject_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [control_index] ADD FOREIGN KEY ([gobject_id]) REFERENCES [gobject] ([gobject_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
9
gr/ddl/tables/data_type.sql
Normal file
9
gr/ddl/tables/data_type.sql
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
-- Table: data_type
|
||||||
|
CREATE TABLE [data_type] (
|
||||||
|
[mx_data_type] tinyint NOT NULL,
|
||||||
|
[description] varchar(30) NOT NULL,
|
||||||
|
[ow_data_type] varchar(10) NULL,
|
||||||
|
CONSTRAINT [PK_data_type] PRIMARY KEY ([mx_data_type])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
8
gr/ddl/tables/deleted_gobject.sql
Normal file
8
gr/ddl/tables/deleted_gobject.sql
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
-- Table: deleted_gobject
|
||||||
|
CREATE TABLE [deleted_gobject] (
|
||||||
|
[gobject_id] int NOT NULL DEFAULT ((0)),
|
||||||
|
[timestamp_of_delete] timestamp NOT NULL,
|
||||||
|
CONSTRAINT [PK_deleted_gobject] PRIMARY KEY ([timestamp_of_delete])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
9
gr/ddl/tables/deleted_ids.sql
Normal file
9
gr/ddl/tables/deleted_ids.sql
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
-- Table: deleted_ids
|
||||||
|
CREATE TABLE [deleted_ids] (
|
||||||
|
[table_id] smallint NULL,
|
||||||
|
[deleted_id] int NOT NULL,
|
||||||
|
[deletion_timestamp] timestamp NOT NULL,
|
||||||
|
[deletion_time] datetime NULL
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
8
gr/ddl/tables/deleted_visual_element.sql
Normal file
8
gr/ddl/tables/deleted_visual_element.sql
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
-- Table: deleted_visual_element
|
||||||
|
CREATE TABLE [deleted_visual_element] (
|
||||||
|
[visual_element_name] nvarchar(329) NULL,
|
||||||
|
[visual_element_type] nvarchar(32) NULL,
|
||||||
|
[timestamp_of_delete] timestamp NOT NULL
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
13
gr/ddl/tables/deleted_visual_element_version.sql
Normal file
13
gr/ddl/tables/deleted_visual_element_version.sql
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
-- Table: deleted_visual_element_version
|
||||||
|
CREATE TABLE [deleted_visual_element_version] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[package_id] int NOT NULL,
|
||||||
|
[mx_primitive_id] smallint NOT NULL,
|
||||||
|
[visual_element_name] nvarchar(329) NOT NULL,
|
||||||
|
[visual_element_type] nvarchar(32) NOT NULL,
|
||||||
|
[timestamp_of_delete] timestamp NOT NULL,
|
||||||
|
[visual_element_id] int NOT NULL,
|
||||||
|
CONSTRAINT [PK_deleted_visual_element_version] PRIMARY KEY ([gobject_id], [package_id], [timestamp_of_delete])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
19
gr/ddl/tables/deployed_file.sql
Normal file
19
gr/ddl/tables/deployed_file.sql
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
-- Table: deployed_file
|
||||||
|
CREATE TABLE [deployed_file] (
|
||||||
|
[deployed_file_id] int NOT NULL,
|
||||||
|
[file_id] int NOT NULL,
|
||||||
|
[node_name] nvarchar(256) NOT NULL,
|
||||||
|
[need_to_delete] int NOT NULL DEFAULT ((0)),
|
||||||
|
[is_package_deployed] bit NOT NULL,
|
||||||
|
[is_editor_deployed] bit NOT NULL,
|
||||||
|
[is_runtime_deployed] bit NOT NULL,
|
||||||
|
[is_browser_deployed] bit NOT NULL,
|
||||||
|
[file_version] nvarchar(50) NOT NULL DEFAULT (''),
|
||||||
|
[file_modified_time] nvarchar(50) NOT NULL DEFAULT (''),
|
||||||
|
CONSTRAINT [PK_deployed_file] PRIMARY KEY ([deployed_file_id], [file_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [deployed_file] ADD FOREIGN KEY ([file_id]) REFERENCES [file_table] ([file_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
8
gr/ddl/tables/deployed_intouch_viewapp.sql
Normal file
8
gr/ddl/tables/deployed_intouch_viewapp.sql
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
-- Table: deployed_intouch_viewapp
|
||||||
|
CREATE TABLE [deployed_intouch_viewapp] (
|
||||||
|
[timestamp_of_deploy] bigint NOT NULL DEFAULT ((1)),
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[deploy_file_transfering] bit NULL DEFAULT ((0))
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
-- Table: deployed_intouch_viewapp_visual_element_dependency
|
||||||
|
CREATE TABLE [deployed_intouch_viewapp_visual_element_dependency] (
|
||||||
|
[gobject_id] int NULL,
|
||||||
|
[visual_element_name] nvarchar(2000) NULL
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
25
gr/ddl/tables/dynamic_attribute.sql
Normal file
25
gr/ddl/tables/dynamic_attribute.sql
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
-- Table: dynamic_attribute
|
||||||
|
CREATE TABLE [dynamic_attribute] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[package_id] int NOT NULL,
|
||||||
|
[mx_primitive_id] smallint NOT NULL,
|
||||||
|
[mx_attribute_id] smallint NOT NULL,
|
||||||
|
[attribute_name] nvarchar(329) NOT NULL,
|
||||||
|
[mx_data_type] smallint NOT NULL,
|
||||||
|
[is_array] bit NOT NULL,
|
||||||
|
[security_classification] smallint NOT NULL,
|
||||||
|
[mx_attribute_category] int NOT NULL,
|
||||||
|
[lock_type] int NOT NULL,
|
||||||
|
[mx_value] text(2147483647) NOT NULL,
|
||||||
|
[owned_by_gobject_id] int NOT NULL DEFAULT ((0)),
|
||||||
|
[original_lock_type] int NOT NULL DEFAULT ((0)),
|
||||||
|
[dynamic_attribute_type] smallint NOT NULL DEFAULT ((0)),
|
||||||
|
[bitvalues] smallint NOT NULL DEFAULT ((0)),
|
||||||
|
[dynamic_attribute_id] bigint NOT NULL,
|
||||||
|
CONSTRAINT [PK_dynamic_attribute] PRIMARY KEY ([gobject_id], [package_id], [mx_primitive_id], [mx_attribute_id], [gobject_id], [mx_primitive_id], [package_id], [gobject_id], [mx_primitive_id], [package_id], [gobject_id], [mx_primitive_id], [package_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [dynamic_attribute] ADD FOREIGN KEY ([package_id]) REFERENCES [primitive_instance] ([package_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
12
gr/ddl/tables/external_content_media_types.sql
Normal file
12
gr/ddl/tables/external_content_media_types.sql
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
-- Table: external_content_media_types
|
||||||
|
CREATE TABLE [external_content_media_types] (
|
||||||
|
[entity_id] int NOT NULL,
|
||||||
|
[media_type] nvarchar(255) NOT NULL,
|
||||||
|
[control_entity_id] int NOT NULL,
|
||||||
|
[uri_property_name] nvarchar(1023) NULL,
|
||||||
|
[media_type_property_name] nvarchar(1023) NULL,
|
||||||
|
[is_default] bit NULL,
|
||||||
|
CONSTRAINT [PK_external_content_media_types] PRIMARY KEY ([entity_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
9
gr/ddl/tables/feature.sql
Normal file
9
gr/ddl/tables/feature.sql
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
-- Table: feature
|
||||||
|
CREATE TABLE [feature] (
|
||||||
|
[feature_id] int NOT NULL,
|
||||||
|
[feature_name] nvarchar(256) NOT NULL,
|
||||||
|
[feature_type] nvarchar(256) NOT NULL,
|
||||||
|
CONSTRAINT [PK_feature] PRIMARY KEY ([feature_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
11
gr/ddl/tables/feature_file_link.sql
Normal file
11
gr/ddl/tables/feature_file_link.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
-- Table: feature_file_link
|
||||||
|
CREATE TABLE [feature_file_link] (
|
||||||
|
[feature_id] int NOT NULL,
|
||||||
|
[file_id] int NOT NULL,
|
||||||
|
CONSTRAINT [PK_feature_file_link] PRIMARY KEY ([feature_id], [file_id], [feature_id], [file_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [feature_file_link] ADD FOREIGN KEY ([file_id]) REFERENCES [file_table] ([file_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
13
gr/ddl/tables/file_browserinfo_link.sql
Normal file
13
gr/ddl/tables/file_browserinfo_link.sql
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
-- Table: file_browserinfo_link
|
||||||
|
CREATE TABLE [file_browserinfo_link] (
|
||||||
|
[primitive_definition_id] int NOT NULL,
|
||||||
|
[file_id] int NOT NULL,
|
||||||
|
[assembly_strong_name] nvarchar(512) NOT NULL,
|
||||||
|
[assembly_type_name] nvarchar(256) NOT NULL,
|
||||||
|
CONSTRAINT [PK_file_browserinfo_link] PRIMARY KEY ([primitive_definition_id], [file_id], [file_id], [primitive_definition_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [file_browserinfo_link] ADD FOREIGN KEY ([primitive_definition_id]) REFERENCES [primitive_definition] ([primitive_definition_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
11
gr/ddl/tables/file_pending_update.sql
Normal file
11
gr/ddl/tables/file_pending_update.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
-- Table: file_pending_update
|
||||||
|
CREATE TABLE [file_pending_update] (
|
||||||
|
[file_id] int NOT NULL,
|
||||||
|
[node_name] nvarchar(256) NOT NULL,
|
||||||
|
CONSTRAINT [PK_file_pending_update] PRIMARY KEY ([file_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [file_pending_update] ADD FOREIGN KEY ([file_id]) REFERENCES [file_table] ([file_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
15
gr/ddl/tables/file_primitive_definition_link.sql
Normal file
15
gr/ddl/tables/file_primitive_definition_link.sql
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
-- Table: file_primitive_definition_link
|
||||||
|
CREATE TABLE [file_primitive_definition_link] (
|
||||||
|
[primitive_definition_id] int NOT NULL,
|
||||||
|
[file_id] int NOT NULL,
|
||||||
|
[is_needed_for_package] bit NOT NULL DEFAULT ((0)),
|
||||||
|
[is_needed_for_runtime] bit NOT NULL DEFAULT ((0)),
|
||||||
|
[is_needed_for_editor] bit NOT NULL DEFAULT ((0)),
|
||||||
|
[is_needed_for_browser] bit NOT NULL DEFAULT ((0)),
|
||||||
|
CONSTRAINT [PK_file_primitive_definition_link] PRIMARY KEY ([primitive_definition_id], [file_id], [file_id], [primitive_definition_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [file_primitive_definition_link] ADD FOREIGN KEY ([primitive_definition_id]) REFERENCES [primitive_definition] ([primitive_definition_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
13
gr/ddl/tables/file_table.sql
Normal file
13
gr/ddl/tables/file_table.sql
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
-- Table: file_table
|
||||||
|
CREATE TABLE [file_table] (
|
||||||
|
[file_id] int NOT NULL,
|
||||||
|
[file_name] nvarchar(256) NOT NULL,
|
||||||
|
[vendor_name] nvarchar(256) NOT NULL,
|
||||||
|
[registration_type] int NOT NULL,
|
||||||
|
[subfolder] nvarchar(256) NOT NULL DEFAULT (''),
|
||||||
|
[file_version] nvarchar(50) NOT NULL DEFAULT (''),
|
||||||
|
[file_modified_time] nvarchar(50) NOT NULL DEFAULT (''),
|
||||||
|
CONSTRAINT [PK_file_table] PRIMARY KEY ([file_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
14
gr/ddl/tables/folder.sql
Normal file
14
gr/ddl/tables/folder.sql
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
-- Table: folder
|
||||||
|
CREATE TABLE [folder] (
|
||||||
|
[folder_id] int NOT NULL,
|
||||||
|
[folder_type] smallint NOT NULL,
|
||||||
|
[folder_name] nvarchar(64) NOT NULL,
|
||||||
|
[parent_folder_id] int NOT NULL,
|
||||||
|
[depth] int NOT NULL,
|
||||||
|
[has_objects] bit NOT NULL,
|
||||||
|
[has_folders] bit NOT NULL,
|
||||||
|
[timestamp_of_last_change] timestamp NOT NULL,
|
||||||
|
CONSTRAINT [PK_folder] PRIMARY KEY ([folder_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
13
gr/ddl/tables/folder_gobject_link.sql
Normal file
13
gr/ddl/tables/folder_gobject_link.sql
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
-- Table: folder_gobject_link
|
||||||
|
CREATE TABLE [folder_gobject_link] (
|
||||||
|
[folder_id] int NOT NULL,
|
||||||
|
[folder_type] smallint NOT NULL,
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[timestamp_of_last_change] timestamp NOT NULL,
|
||||||
|
CONSTRAINT [PK_folder_gobject_link] PRIMARY KEY ([folder_id], [gobject_id], [gobject_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [folder_gobject_link] ADD FOREIGN KEY ([gobject_id]) REFERENCES [gobject] ([gobject_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
18
gr/ddl/tables/galaxy.sql
Normal file
18
gr/ddl/tables/galaxy.sql
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
-- Table: galaxy
|
||||||
|
CREATE TABLE [galaxy] (
|
||||||
|
[time_of_last_deploy] datetime NULL DEFAULT (getdate()),
|
||||||
|
[time_of_last_config_change] datetime NULL DEFAULT (getdate()),
|
||||||
|
[is_galaxy_installed] bit NOT NULL DEFAULT ((1)),
|
||||||
|
[time_of_last_reference_binding] datetime NULL DEFAULT (getdate()),
|
||||||
|
[timestamp_of_last_cascade] bigint NOT NULL DEFAULT ((1)),
|
||||||
|
[timestamp_of_last_visual_element_reference_bind] bigint NOT NULL DEFAULT ((0)),
|
||||||
|
[max_proxy_timestamp] bigint NOT NULL DEFAULT (CONVERT([bigint],@@dbts)),
|
||||||
|
[max_visual_element_timestamp] bigint NOT NULL DEFAULT (CONVERT([bigint],@@dbts)),
|
||||||
|
[is_migration_in_progress] bit NOT NULL DEFAULT ((0)),
|
||||||
|
[time_of_last_association_change] datetime NULL DEFAULT (getdate()),
|
||||||
|
[subscription_id] uniqueidentifier NULL,
|
||||||
|
[batch_id] uniqueidentifier NULL,
|
||||||
|
[iteration_id] int NOT NULL DEFAULT ((0))
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
7
gr/ddl/tables/galaxy_data.sql
Normal file
7
gr/ddl/tables/galaxy_data.sql
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
-- Table: galaxy_data
|
||||||
|
CREATE TABLE [galaxy_data] (
|
||||||
|
[data_type] nvarchar(256) NOT NULL,
|
||||||
|
[data] image(2147483647) NULL
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
8
gr/ddl/tables/galaxy_settings.sql
Normal file
8
gr/ddl/tables/galaxy_settings.sql
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
-- Table: galaxy_settings
|
||||||
|
CREATE TABLE [galaxy_settings] (
|
||||||
|
[galaxyid] int NULL,
|
||||||
|
[default_qs_data] ntext(1073741823) NOT NULL,
|
||||||
|
[current_qs_data] ntext(1073741823) NOT NULL
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
35
gr/ddl/tables/gobject.sql
Normal file
35
gr/ddl/tables/gobject.sql
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
-- Table: gobject
|
||||||
|
CREATE TABLE [gobject] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[template_definition_id] int NOT NULL,
|
||||||
|
[derived_from_gobject_id] int NOT NULL DEFAULT ((0)),
|
||||||
|
[contained_by_gobject_id] int NOT NULL DEFAULT ((0)),
|
||||||
|
[area_gobject_id] int NOT NULL DEFAULT ((0)),
|
||||||
|
[hosted_by_gobject_id] int NOT NULL DEFAULT ((0)),
|
||||||
|
[checked_out_by_user_guid] uniqueidentifier NULL,
|
||||||
|
[default_symbol_gobject_id] int NOT NULL DEFAULT ((0)),
|
||||||
|
[default_display_gobject_id] int NOT NULL DEFAULT ((0)),
|
||||||
|
[checked_in_package_id] int NOT NULL DEFAULT ((0)),
|
||||||
|
[checked_out_package_id] int NOT NULL DEFAULT ((0)),
|
||||||
|
[deployed_package_id] int NOT NULL DEFAULT ((0)),
|
||||||
|
[last_deployed_package_id] int NOT NULL DEFAULT ((0)),
|
||||||
|
[tag_name] nvarchar(329) NOT NULL,
|
||||||
|
[contained_name] nvarchar(32) NOT NULL DEFAULT (''),
|
||||||
|
[identity_guid] uniqueidentifier NOT NULL DEFAULT (newid()),
|
||||||
|
[configuration_guid] uniqueidentifier NOT NULL,
|
||||||
|
[configuration_version] int NOT NULL,
|
||||||
|
[deployed_version] int NOT NULL DEFAULT ((0)),
|
||||||
|
[is_template] bit NOT NULL DEFAULT ((0)),
|
||||||
|
[is_hidden] bit NOT NULL DEFAULT ((0)),
|
||||||
|
[software_upgrade_needed] bit NOT NULL DEFAULT ((0)),
|
||||||
|
[hosting_tree_level] smallint NOT NULL DEFAULT ((0)),
|
||||||
|
[hierarchical_name] nvarchar(329) NOT NULL DEFAULT (''),
|
||||||
|
[namespace_id] smallint NOT NULL DEFAULT ((1)),
|
||||||
|
[deployment_pending_status] bit NOT NULL DEFAULT ((0)),
|
||||||
|
CONSTRAINT [PK_gobject] PRIMARY KEY ([gobject_id], [namespace_id], [template_definition_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [gobject] ADD FOREIGN KEY ([template_definition_id]) REFERENCES [template_definition] ([template_definition_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
11
gr/ddl/tables/gobject_asset_order.sql
Normal file
11
gr/ddl/tables/gobject_asset_order.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
-- Table: gobject_asset_order
|
||||||
|
CREATE TABLE [gobject_asset_order] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[relative_index] float(53,) NOT NULL,
|
||||||
|
CONSTRAINT [PK_gobject_asset_order] PRIMARY KEY ([gobject_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [gobject_asset_order] ADD FOREIGN KEY ([gobject_id]) REFERENCES [gobject] ([gobject_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
16
gr/ddl/tables/gobject_change_log.sql
Normal file
16
gr/ddl/tables/gobject_change_log.sql
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
-- Table: gobject_change_log
|
||||||
|
CREATE TABLE [gobject_change_log] (
|
||||||
|
[gobject_change_log_id] int NOT NULL,
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[change_date] datetime NULL,
|
||||||
|
[operation_id] smallint NOT NULL,
|
||||||
|
[user_comment] nvarchar(1024) NOT NULL DEFAULT (''),
|
||||||
|
[configuration_version] int NOT NULL DEFAULT ((0)),
|
||||||
|
[user_profile_name] nvarchar(256) NOT NULL,
|
||||||
|
CONSTRAINT [PK_gobject_change_log] PRIMARY KEY ([gobject_id], [operation_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [gobject_change_log] ADD FOREIGN KEY ([operation_id]) REFERENCES [lookup_operation] ([operation_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
11
gr/ddl/tables/gobject_filter_info_timestamp.sql
Normal file
11
gr/ddl/tables/gobject_filter_info_timestamp.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
-- Table: gobject_filter_info_timestamp
|
||||||
|
CREATE TABLE [gobject_filter_info_timestamp] (
|
||||||
|
[gobject_id] int NULL,
|
||||||
|
[timestamp_of_last_change] timestamp NOT NULL,
|
||||||
|
CONSTRAINT [PK_gobject_filter_info_timestamp] PRIMARY KEY ([timestamp_of_last_change], [gobject_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [gobject_filter_info_timestamp] ADD FOREIGN KEY ([gobject_id]) REFERENCES [gobject] ([gobject_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
11
gr/ddl/tables/gobject_friendly_name.sql
Normal file
11
gr/ddl/tables/gobject_friendly_name.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
-- Table: gobject_friendly_name
|
||||||
|
CREATE TABLE [gobject_friendly_name] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[friendly_name] nvarchar(1024) NOT NULL DEFAULT (''),
|
||||||
|
CONSTRAINT [PK_gobject_friendly_name] PRIMARY KEY ([gobject_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [gobject_friendly_name] ADD FOREIGN KEY ([gobject_id]) REFERENCES [gobject] ([gobject_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
7
gr/ddl/tables/gobject_log_details.sql
Normal file
7
gr/ddl/tables/gobject_log_details.sql
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
-- Table: gobject_log_details
|
||||||
|
CREATE TABLE [gobject_log_details] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[tag_name] nvarchar(329) NOT NULL
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
6
gr/ddl/tables/gobject_protected.sql
Normal file
6
gr/ddl/tables/gobject_protected.sql
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
-- Table: gobject_protected
|
||||||
|
CREATE TABLE [gobject_protected] (
|
||||||
|
[gobject_id] int NOT NULL
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
13
gr/ddl/tables/instance.sql
Normal file
13
gr/ddl/tables/instance.sql
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
-- Table: instance
|
||||||
|
CREATE TABLE [instance] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[mx_platform_id] smallint NOT NULL DEFAULT ((0)),
|
||||||
|
[mx_engine_id] smallint NOT NULL DEFAULT ((0)),
|
||||||
|
[mx_object_id] smallint NOT NULL DEFAULT ((0)),
|
||||||
|
CONSTRAINT [PK_instance] PRIMARY KEY ([gobject_id], [gobject_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [instance] ADD FOREIGN KEY ([gobject_id]) REFERENCES [gobject] ([gobject_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
6
gr/ddl/tables/intouchviewapptemplate_allsymbols.sql
Normal file
6
gr/ddl/tables/intouchviewapptemplate_allsymbols.sql
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
-- Table: intouchviewapptemplate_allsymbols
|
||||||
|
CREATE TABLE [intouchviewapptemplate_allsymbols] (
|
||||||
|
[gobject_id] int NOT NULL
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
8
gr/ddl/tables/lookup_category.sql
Normal file
8
gr/ddl/tables/lookup_category.sql
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
-- Table: lookup_category
|
||||||
|
CREATE TABLE [lookup_category] (
|
||||||
|
[category_id] smallint NOT NULL,
|
||||||
|
[category_name] nvarchar(50) NOT NULL,
|
||||||
|
CONSTRAINT [PK_lookup_category] PRIMARY KEY ([category_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
7
gr/ddl/tables/lookup_folder.sql
Normal file
7
gr/ddl/tables/lookup_folder.sql
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
-- Table: lookup_folder
|
||||||
|
CREATE TABLE [lookup_folder] (
|
||||||
|
[folder_type] smallint NOT NULL,
|
||||||
|
[folder_type_name] nvarchar(32) NULL
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
9
gr/ddl/tables/lookup_operation.sql
Normal file
9
gr/ddl/tables/lookup_operation.sql
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
-- Table: lookup_operation
|
||||||
|
CREATE TABLE [lookup_operation] (
|
||||||
|
[operation_id] smallint NOT NULL,
|
||||||
|
[operation_code] nvarchar(50) NOT NULL,
|
||||||
|
[operation_name] nvarchar(256) NOT NULL,
|
||||||
|
CONSTRAINT [PK_lookup_operation] PRIMARY KEY ([operation_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
8
gr/ddl/tables/lookup_package_op_status.sql
Normal file
8
gr/ddl/tables/lookup_package_op_status.sql
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
-- Table: lookup_package_op_status
|
||||||
|
CREATE TABLE [lookup_package_op_status] (
|
||||||
|
[status_id] int NOT NULL,
|
||||||
|
[status_name] nvarchar(50) NOT NULL,
|
||||||
|
CONSTRAINT [PK_lookup_package_op_status] PRIMARY KEY ([status_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
8
gr/ddl/tables/lookup_status.sql
Normal file
8
gr/ddl/tables/lookup_status.sql
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
-- Table: lookup_status
|
||||||
|
CREATE TABLE [lookup_status] (
|
||||||
|
[status_id] int NOT NULL,
|
||||||
|
[status_name] nvarchar(50) NOT NULL,
|
||||||
|
CONSTRAINT [PK_lookup_status] PRIMARY KEY ([status_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
7
gr/ddl/tables/lookup_table_name.sql
Normal file
7
gr/ddl/tables/lookup_table_name.sql
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
-- Table: lookup_table_name
|
||||||
|
CREATE TABLE [lookup_table_name] (
|
||||||
|
[table_id] smallint NOT NULL,
|
||||||
|
[table_name] nvarchar(250) NULL
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
8
gr/ddl/tables/namespace.sql
Normal file
8
gr/ddl/tables/namespace.sql
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
-- Table: namespace
|
||||||
|
CREATE TABLE [namespace] (
|
||||||
|
[namespace_id] smallint NOT NULL,
|
||||||
|
[namespace_name] nvarchar(32) NULL,
|
||||||
|
CONSTRAINT [PK_namespace] PRIMARY KEY ([namespace_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
9
gr/ddl/tables/object_device_linkage.sql
Normal file
9
gr/ddl/tables/object_device_linkage.sql
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
-- Table: object_device_linkage
|
||||||
|
CREATE TABLE [object_device_linkage] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[dio_id] int NOT NULL,
|
||||||
|
[sg_mx_primitive_id] smallint NOT NULL,
|
||||||
|
CONSTRAINT [PK_object_device_linkage] PRIMARY KEY ([gobject_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
9
gr/ddl/tables/object_wizard_overview_symbols.sql
Normal file
9
gr/ddl/tables/object_wizard_overview_symbols.sql
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
-- Table: object_wizard_overview_symbols
|
||||||
|
CREATE TABLE [object_wizard_overview_symbols] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[visual_element_id] int NOT NULL,
|
||||||
|
[change_type] int NOT NULL,
|
||||||
|
[mx_primitive_id] int NULL
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
12
gr/ddl/tables/object_wizard_symbol_override.sql
Normal file
12
gr/ddl/tables/object_wizard_symbol_override.sql
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
-- Table: object_wizard_symbol_override
|
||||||
|
CREATE TABLE [object_wizard_symbol_override] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[package_id] int NOT NULL,
|
||||||
|
[symbol_overrides] image(2147483647) NULL,
|
||||||
|
CONSTRAINT [PK_object_wizard_symbol_override] PRIMARY KEY ([gobject_id], [package_id], [gobject_id], [package_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [object_wizard_symbol_override] ADD FOREIGN KEY ([package_id]) REFERENCES [package] ([package_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
15
gr/ddl/tables/object_wizard_symbol_override_mapping.sql
Normal file
15
gr/ddl/tables/object_wizard_symbol_override_mapping.sql
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
-- Table: object_wizard_symbol_override_mapping
|
||||||
|
CREATE TABLE [object_wizard_symbol_override_mapping] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[package_id] int NOT NULL,
|
||||||
|
[mx_primitive_id] smallint NOT NULL,
|
||||||
|
[has_local_overrides] bit NOT NULL,
|
||||||
|
[thumbnail] image(2147483647) NULL,
|
||||||
|
[preview] image(2147483647) NULL,
|
||||||
|
CONSTRAINT [PK_object_wizard_symbol_override_mapping] PRIMARY KEY ([gobject_id], [package_id], [mx_primitive_id], [gobject_id], [mx_primitive_id], [package_id], [gobject_id], [mx_primitive_id], [package_id], [gobject_id], [mx_primitive_id], [package_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [object_wizard_symbol_override_mapping] ADD FOREIGN KEY ([package_id]) REFERENCES [primitive_instance] ([package_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
9
gr/ddl/tables/old_checked_in_packages.sql
Normal file
9
gr/ddl/tables/old_checked_in_packages.sql
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
-- Table: old_checked_in_packages
|
||||||
|
CREATE TABLE [old_checked_in_packages] (
|
||||||
|
[package_id] int NULL,
|
||||||
|
[gobject_id] int NULL,
|
||||||
|
[is_template] bit NULL,
|
||||||
|
[is_being_referenced] bit NULL DEFAULT ((0))
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
13
gr/ddl/tables/operation.sql
Normal file
13
gr/ddl/tables/operation.sql
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
-- Table: operation
|
||||||
|
CREATE TABLE [operation] (
|
||||||
|
[operation_id] int NOT NULL,
|
||||||
|
[user_profile_id] int NOT NULL,
|
||||||
|
[operation_name] nvarchar(300) NOT NULL,
|
||||||
|
[start_time] datetime NOT NULL DEFAULT (getdate()),
|
||||||
|
CONSTRAINT [PK_operation] PRIMARY KEY ([operation_id], [user_profile_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [operation] ADD FOREIGN KEY ([user_profile_id]) REFERENCES [user_profile] ([user_profile_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
13
gr/ddl/tables/operation_message.sql
Normal file
13
gr/ddl/tables/operation_message.sql
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
-- Table: operation_message
|
||||||
|
CREATE TABLE [operation_message] (
|
||||||
|
[message_id] int NOT NULL,
|
||||||
|
[operation_id] int NOT NULL,
|
||||||
|
[message_text] nvarchar(300) NOT NULL,
|
||||||
|
[message_time] datetime NOT NULL DEFAULT (getdate()),
|
||||||
|
CONSTRAINT [PK_operation_message] PRIMARY KEY ([message_id], [operation_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [operation_message] ADD FOREIGN KEY ([operation_id]) REFERENCES [operation] ([operation_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
11
gr/ddl/tables/operation_status.sql
Normal file
11
gr/ddl/tables/operation_status.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
-- Table: operation_status
|
||||||
|
CREATE TABLE [operation_status] (
|
||||||
|
[operation_id] int NOT NULL,
|
||||||
|
[status] int NOT NULL,
|
||||||
|
CONSTRAINT [PK_operation_status] PRIMARY KEY ([operation_id], [status], [operation_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [operation_status] ADD FOREIGN KEY ([operation_id]) REFERENCES [operation] ([operation_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
8
gr/ddl/tables/operation_status_look_up.sql
Normal file
8
gr/ddl/tables/operation_status_look_up.sql
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
-- Table: operation_status_look_up
|
||||||
|
CREATE TABLE [operation_status_look_up] (
|
||||||
|
[status] int NOT NULL,
|
||||||
|
[status_name] varchar(100) NOT NULL,
|
||||||
|
CONSTRAINT [PK_operation_status_look_up] PRIMARY KEY ([status])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
16
gr/ddl/tables/ow_group_def.sql
Normal file
16
gr/ddl/tables/ow_group_def.sql
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
-- Table: ow_group_def
|
||||||
|
CREATE TABLE [ow_group_def] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[package_id] int NOT NULL,
|
||||||
|
[ow_group_id] int NOT NULL,
|
||||||
|
[prompt] nvarchar(260) NOT NULL,
|
||||||
|
[GUID] varchar(36) NOT NULL,
|
||||||
|
[description] nvarchar(260) NULL,
|
||||||
|
[visibility_rules] nvarchar(-1) NULL,
|
||||||
|
CONSTRAINT [PK_ow_group_def] PRIMARY KEY ([gobject_id], [package_id], [ow_group_id], [ow_group_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [ow_group_def] ADD FOREIGN KEY ([ow_group_id]) REFERENCES [ow_group_id] ([ow_group_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
7
gr/ddl/tables/ow_group_id.sql
Normal file
7
gr/ddl/tables/ow_group_id.sql
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
-- Table: ow_group_id
|
||||||
|
CREATE TABLE [ow_group_id] (
|
||||||
|
[ow_group_id] int NOT NULL,
|
||||||
|
CONSTRAINT [PK_ow_group_id] PRIMARY KEY ([ow_group_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
13
gr/ddl/tables/ow_group_override.sql
Normal file
13
gr/ddl/tables/ow_group_override.sql
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
-- Table: ow_group_override
|
||||||
|
CREATE TABLE [ow_group_override] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[package_id] int NOT NULL,
|
||||||
|
[ow_group_id] int NOT NULL,
|
||||||
|
[property_bitmask] int NOT NULL,
|
||||||
|
CONSTRAINT [PK_ow_group_override] PRIMARY KEY ([gobject_id], [package_id], [ow_group_id], [ow_group_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [ow_group_override] ADD FOREIGN KEY ([ow_group_id]) REFERENCES [ow_group_id] ([ow_group_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
17
gr/ddl/tables/ow_instance_setting.sql
Normal file
17
gr/ddl/tables/ow_instance_setting.sql
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
-- Table: ow_instance_setting
|
||||||
|
CREATE TABLE [ow_instance_setting] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[package_id] int NOT NULL,
|
||||||
|
[name] nvarchar(329) NOT NULL,
|
||||||
|
[ow_setting_type] int NOT NULL,
|
||||||
|
[is_dmv] bit NOT NULL,
|
||||||
|
[override_value] nvarchar(-1) NULL,
|
||||||
|
[is_mx] bit NOT NULL,
|
||||||
|
[is_implicit] bit NULL,
|
||||||
|
CONSTRAINT [PK_ow_instance_setting] PRIMARY KEY ([gobject_id], [package_id], [name], [ow_setting_type], [is_dmv], [ow_setting_type])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [ow_instance_setting] ADD FOREIGN KEY ([ow_setting_type]) REFERENCES [ow_lu_setting] ([ow_setting_type]);
|
||||||
|
GO
|
||||||
|
|
||||||
19
gr/ddl/tables/ow_link_def.sql
Normal file
19
gr/ddl/tables/ow_link_def.sql
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
-- Table: ow_link_def
|
||||||
|
CREATE TABLE [ow_link_def] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[package_id] int NOT NULL,
|
||||||
|
[ow_link_id] int NOT NULL,
|
||||||
|
[ow_link_type] int NOT NULL,
|
||||||
|
[name] nvarchar(329) NOT NULL,
|
||||||
|
[mx_primitive_id] smallint NULL,
|
||||||
|
[dynamic_attribute_id] bigint NULL,
|
||||||
|
[optional_id] int NULL,
|
||||||
|
[property_bitmask] int NOT NULL,
|
||||||
|
[sort] int NOT NULL,
|
||||||
|
CONSTRAINT [PK_ow_link_def] PRIMARY KEY ([gobject_id], [package_id], [ow_link_id], [ow_link_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [ow_link_def] ADD FOREIGN KEY ([ow_link_id]) REFERENCES [ow_link_id] ([ow_link_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
11
gr/ddl/tables/ow_link_id.sql
Normal file
11
gr/ddl/tables/ow_link_id.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
-- Table: ow_link_id
|
||||||
|
CREATE TABLE [ow_link_id] (
|
||||||
|
[ow_link_id] int NOT NULL,
|
||||||
|
[ow_opt_or_choice_id] int NOT NULL,
|
||||||
|
CONSTRAINT [PK_ow_link_id] PRIMARY KEY ([ow_link_id], [ow_opt_or_choice_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [ow_link_id] ADD FOREIGN KEY ([ow_opt_or_choice_id]) REFERENCES [ow_opt_or_choice_id] ([ow_opt_or_choice_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
17
gr/ddl/tables/ow_lu_definition.sql
Normal file
17
gr/ddl/tables/ow_lu_definition.sql
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
-- Table: ow_lu_definition
|
||||||
|
CREATE TABLE [ow_lu_definition] (
|
||||||
|
[ow_setting_type] int NOT NULL,
|
||||||
|
[primitive_definition_id] int NOT NULL,
|
||||||
|
[template_definition_id] int NOT NULL,
|
||||||
|
[boolean_analog] bit NOT NULL,
|
||||||
|
[ext] varchar(20) NOT NULL,
|
||||||
|
[link_setting] int NULL,
|
||||||
|
[link_feature] int NULL,
|
||||||
|
[mx_attribute_id] smallint NULL,
|
||||||
|
CONSTRAINT [PK_ow_lu_definition] PRIMARY KEY ([ow_setting_type], [primitive_definition_id], [ow_setting_type])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [ow_lu_definition] ADD FOREIGN KEY ([ow_setting_type]) REFERENCES [ow_lu_setting] ([ow_setting_type]);
|
||||||
|
GO
|
||||||
|
|
||||||
18
gr/ddl/tables/ow_lu_setting.sql
Normal file
18
gr/ddl/tables/ow_lu_setting.sql
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
-- Table: ow_lu_setting
|
||||||
|
CREATE TABLE [ow_lu_setting] (
|
||||||
|
[ow_setting_type] int NOT NULL,
|
||||||
|
[category] varchar(10) NOT NULL,
|
||||||
|
[mx_data_type] tinyint NULL,
|
||||||
|
[setting_name] nvarchar(30) NOT NULL,
|
||||||
|
[feature_id] int NULL,
|
||||||
|
[feature_sub_id] int NULL,
|
||||||
|
[parent_type] int NULL,
|
||||||
|
[no_ext] bit NULL,
|
||||||
|
[raw_value] nvarchar(100) NULL,
|
||||||
|
CONSTRAINT [PK_ow_lu_setting] PRIMARY KEY ([ow_setting_type], [mx_data_type])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [ow_lu_setting] ADD FOREIGN KEY ([mx_data_type]) REFERENCES [data_type] ([mx_data_type]);
|
||||||
|
GO
|
||||||
|
|
||||||
21
gr/ddl/tables/ow_opt_or_choice_def.sql
Normal file
21
gr/ddl/tables/ow_opt_or_choice_def.sql
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
-- Table: ow_opt_or_choice_def
|
||||||
|
CREATE TABLE [ow_opt_or_choice_def] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[package_id] int NOT NULL,
|
||||||
|
[ow_opt_or_choice_id] int NOT NULL,
|
||||||
|
[prompt] nvarchar(260) NOT NULL,
|
||||||
|
[GUID] varchar(36) NOT NULL,
|
||||||
|
[name] varchar(10) NOT NULL,
|
||||||
|
[description] nvarchar(260) NULL,
|
||||||
|
[choice_sequence_number] int NULL,
|
||||||
|
[after_group_or_option] int NULL,
|
||||||
|
[initial_value] bit NOT NULL DEFAULT ((0)),
|
||||||
|
[visibility_rules] nvarchar(-1) NULL,
|
||||||
|
[sort] int NOT NULL,
|
||||||
|
CONSTRAINT [PK_ow_opt_or_choice_def] PRIMARY KEY ([gobject_id], [package_id], [ow_opt_or_choice_id], [ow_opt_or_choice_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [ow_opt_or_choice_def] ADD FOREIGN KEY ([ow_opt_or_choice_id]) REFERENCES [ow_opt_or_choice_id] ([ow_opt_or_choice_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
11
gr/ddl/tables/ow_opt_or_choice_id.sql
Normal file
11
gr/ddl/tables/ow_opt_or_choice_id.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
-- Table: ow_opt_or_choice_id
|
||||||
|
CREATE TABLE [ow_opt_or_choice_id] (
|
||||||
|
[ow_opt_or_choice_id] int NOT NULL,
|
||||||
|
[ow_group_id] int NOT NULL,
|
||||||
|
CONSTRAINT [PK_ow_opt_or_choice_id] PRIMARY KEY ([ow_opt_or_choice_id], [ow_group_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [ow_opt_or_choice_id] ADD FOREIGN KEY ([ow_group_id]) REFERENCES [ow_group_id] ([ow_group_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
14
gr/ddl/tables/ow_opt_or_choice_override.sql
Normal file
14
gr/ddl/tables/ow_opt_or_choice_override.sql
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
-- Table: ow_opt_or_choice_override
|
||||||
|
CREATE TABLE [ow_opt_or_choice_override] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[package_id] int NOT NULL,
|
||||||
|
[ow_opt_or_choice_id] int NOT NULL,
|
||||||
|
[property_bitmask] int NULL,
|
||||||
|
[override_value] bit NULL,
|
||||||
|
CONSTRAINT [PK_ow_opt_or_choice_override] PRIMARY KEY ([gobject_id], [package_id], [ow_opt_or_choice_id], [ow_opt_or_choice_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [ow_opt_or_choice_override] ADD FOREIGN KEY ([ow_opt_or_choice_id]) REFERENCES [ow_opt_or_choice_id] ([ow_opt_or_choice_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
18
gr/ddl/tables/ow_setting_def.sql
Normal file
18
gr/ddl/tables/ow_setting_def.sql
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
-- Table: ow_setting_def
|
||||||
|
CREATE TABLE [ow_setting_def] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[package_id] int NOT NULL,
|
||||||
|
[ow_setting_id] int NOT NULL,
|
||||||
|
[ow_setting_type] int NOT NULL,
|
||||||
|
[reference] nvarchar(260) NULL,
|
||||||
|
[property_id] varchar(36) NULL,
|
||||||
|
[initial_value] nvarchar(-1) NULL,
|
||||||
|
[property_bitmask] int NULL,
|
||||||
|
[sort] int NOT NULL,
|
||||||
|
CONSTRAINT [PK_ow_setting_def] PRIMARY KEY ([gobject_id], [package_id], [ow_setting_id], [ow_setting_type], [ow_setting_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [ow_setting_def] ADD FOREIGN KEY ([ow_setting_id]) REFERENCES [ow_setting_id] ([ow_setting_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
11
gr/ddl/tables/ow_setting_id.sql
Normal file
11
gr/ddl/tables/ow_setting_id.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
-- Table: ow_setting_id
|
||||||
|
CREATE TABLE [ow_setting_id] (
|
||||||
|
[ow_setting_id] int NOT NULL,
|
||||||
|
[ow_link_id] int NOT NULL,
|
||||||
|
CONSTRAINT [PK_ow_setting_id] PRIMARY KEY ([ow_setting_id], [ow_link_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [ow_setting_id] ADD FOREIGN KEY ([ow_link_id]) REFERENCES [ow_link_id] ([ow_link_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
14
gr/ddl/tables/ow_setting_override.sql
Normal file
14
gr/ddl/tables/ow_setting_override.sql
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
-- Table: ow_setting_override
|
||||||
|
CREATE TABLE [ow_setting_override] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[package_id] int NOT NULL,
|
||||||
|
[ow_setting_id] int NOT NULL,
|
||||||
|
[override_value] nvarchar(-1) NULL,
|
||||||
|
[property_bitmask] int NULL,
|
||||||
|
CONSTRAINT [PK_ow_setting_override] PRIMARY KEY ([gobject_id], [package_id], [ow_setting_id], [ow_setting_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [ow_setting_override] ADD FOREIGN KEY ([ow_setting_id]) REFERENCES [ow_setting_id] ([ow_setting_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
17
gr/ddl/tables/ow_symbol_setting.sql
Normal file
17
gr/ddl/tables/ow_symbol_setting.sql
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
-- Table: ow_symbol_setting
|
||||||
|
CREATE TABLE [ow_symbol_setting] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[package_id] int NOT NULL,
|
||||||
|
[sym_name] nvarchar(33) NOT NULL,
|
||||||
|
[ow_setting_type] int NOT NULL,
|
||||||
|
[reference] nvarchar(260) NOT NULL,
|
||||||
|
[property_id] nvarchar(36) NULL,
|
||||||
|
[value] nvarchar(-1) NULL,
|
||||||
|
[property_bitmask] int NULL,
|
||||||
|
CONSTRAINT [PK_ow_symbol_setting] PRIMARY KEY ([gobject_id], [package_id], [sym_name], [ow_setting_type], [reference], [ow_setting_type])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [ow_symbol_setting] ADD FOREIGN KEY ([ow_setting_type]) REFERENCES [ow_lu_setting] ([ow_setting_type]);
|
||||||
|
GO
|
||||||
|
|
||||||
21
gr/ddl/tables/owned_visual_element.sql
Normal file
21
gr/ddl/tables/owned_visual_element.sql
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
-- Table: owned_visual_element
|
||||||
|
CREATE TABLE [owned_visual_element] (
|
||||||
|
[gobject_id] int NOT NULL,
|
||||||
|
[package_id] int NOT NULL,
|
||||||
|
[mx_primitive_id] smallint NOT NULL,
|
||||||
|
[visual_element_id] int NOT NULL,
|
||||||
|
[thumbnail] image(2147483647) NULL,
|
||||||
|
[description] nvarchar(1024) NULL,
|
||||||
|
[visual_element_definition] image(2147483647) NOT NULL,
|
||||||
|
[is_thumbnail_dirty] bit NOT NULL DEFAULT ((0)),
|
||||||
|
[visual_element_definition_grm] image(2147483647) NOT NULL,
|
||||||
|
[content_type] nvarchar(1024) NULL,
|
||||||
|
[visual_element_crossRef] nvarchar(-1) NULL,
|
||||||
|
[preview] image(2147483647) NULL,
|
||||||
|
CONSTRAINT [PK_owned_visual_element] PRIMARY KEY ([gobject_id], [package_id], [mx_primitive_id], [gobject_id], [mx_primitive_id], [package_id], [gobject_id], [mx_primitive_id], [package_id], [gobject_id], [mx_primitive_id], [package_id])
|
||||||
|
);
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER TABLE [owned_visual_element] ADD FOREIGN KEY ([package_id]) REFERENCES [visual_element_version] ([package_id]);
|
||||||
|
GO
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user