This commit is contained in:
2026-04-17 14:55:32 -04:00
commit bc3ac1d4c9
18017 changed files with 4371742 additions and 0 deletions

View File

@@ -0,0 +1,125 @@
---
source_files:
- DataPRO/DbAPI/Channels/IChannels.cs
- DataPRO/DbAPI/Channels/Channels.cs
generated_at: "2026-04-16T04:27:22.495313+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "0eeac4044bafa708"
---
# Documentation: `DbAPI.Channels.IChannels` / `Channels`
## 1. Purpose
This module provides database access layer (DAL) functionality for managing channel-related data in the system, including channel definitions (`Channels` table), channel codes (e.g., ISO codes, user codes), channel settings (both global and group-specific), and channel code types. It serves as the primary interface for CRUD operations on channel data, enforcing authentication, validating parameters, and translating between .NET objects and SQL Server stored procedures. The module is part of the `DbAPI` namespace and is intended for internal use by higher-level components that need to persist or retrieve channel configuration data.
## 2. Public Interface
All methods are defined in the `IChannels` interface and implemented by the internal class `Channels`. Return values are `ulong`, where `0` (`ERROR_SUCCESS`) indicates success and non-zero values indicate error codes (e.g., from `ErrorCodes`).
### Channel Codes
- **`ulong ChannelCodesInsert(IUserDbRecord user, IConnectionDetails connection, IReadOnlyDictionary<ChannelEnumsAndConstants.ChannelCodeType, short> lookup, IChannelCode channelCode, out int id)`**
Inserts a new channel code record. On success, `id` is populated with the new database ID. Requires `channelCode.Code` and `channelCode.Name` to be non-null/non-empty.
- **`ulong ChannelCodesUpdate(IUserDbRecord user, IConnectionDetails connection, IReadOnlyDictionary<ChannelEnumsAndConstants.ChannelCodeType, short> lookup, IChannelCode channelCode)`**
Updates an existing channel code record. Requires `channelCode.Id`, `Code`, and `Name` to be non-null/non-empty.
- **`ulong ChannelCodesDelete(IUserDbRecord user, IConnectionDetails connection, int? id, string code, string name, int? codeType)`**
Deletes channel code records matching any combination of `id`, `code`, `name`, or `codeType`. All parameters are optional; null means “match all”.
- **`ulong ChannelCodeTypesGet(IUserDbRecord user, IConnectionDetails connection, short? id, string codeType, out Tuple<short, string>[] records)`**
Retrieves channel code type definitions (ID and string name). Parameters are filters; null means “match all”.
- **`ulong ChannelCodesGet(IUserDbRecord user, IConnectionDetails connection, int? Id, string code, string name, ChannelEnumsAndConstants.ChannelCodeType? codeType, IReadOnlyDictionary<short, string> channelTypeLookup, out IChannelCode[] records)`**
Retrieves channel code records matching filters. `channelTypeLookup` is required (cannot be null) and maps code type IDs to names for deserialization.
### Channel Settings
- **`ulong ChannelSettingsUpdate(IUserDbRecord user, IConnectionDetails connection, int settingId, string defaultValue)`**
Updates the `DefaultValue` of a channel setting (by `settingId`).
- **`ulong ChannelSettingsGet(IUserDbRecord user, IConnectionDetails connection, int? settingId, string settingName, out IChannelSettingRecord[] records)`**
Retrieves channel settings matching `settingId` and/or `settingName`. Parameters are optional.
- **`ulong GroupChannelSettingsDelete(IUserDbRecord user, IConnectionDetails connection, long channelId, int? settingId)`**
Deletes group channel settings for a given `channelId`. If `settingId` is null, deletes *all* settings for the channel.
- **`ulong GroupChannelSettingsInsert(IUserDbRecord user, IConnectionDetails connection, int clientDbVersion, long channelId, IGroupChannelSettingRecord record)`**
Inserts a new group channel setting. Uses version-aware stored procedure resolution via `Database.Database.PrepareForDbAccess`.
- **`ulong GroupChannelSettingsGet(IUserDbRecord user, IConnectionDetails connection, int clientDbVersion, List<long> channelIdList, out IGroupChannelSettingRecord[] records, out string[] errors)`**
Retrieves group channel settings for a list of channel IDs. Behavior differs based on `clientDbVersion`:
- If `< Constants.BULK_GROUPCHANNELSETTINGS_GET_DB_VERSION`: calls `sp_GroupChannelSettingsGet` repeatedly (one per channel).
- Otherwise: calls a bulk version using a table-valued parameter (`dbo.StringList`).
`errors` is always initialized to an empty array (no errors are populated in current implementation).
### Channels (Main Table)
- **`ulong ChannelsInsert(IUserDbRecord user, IConnectionDetails connection, ref IChannelDbRecord channel)`**
Inserts a new channel record. On success, `channel.Id` is updated with the new database ID. Nullable fields (`DASId`, `SensorId`) are handled conditionally (only added to command if > 0). `IsoCode`, `IsoChannelName`, `UserCode`, `UserChannelName` default to `""` if null.
- **`ulong ChannelsUpdate(IUserDbRecord user, IConnectionDetails connection, IChannelDbRecord channel)`**
Updates an existing channel record. Requires `channel.Id`.
- **`ulong ChannelsGet(IUserDbRecord user, IConnectionDetails connection, int clientDbVersion, long? channelId, int? groupId, int? dasId, int? sensorId, int? testSetupId, string testSetupName, out IChannelDbRecord[] channels)`**
Retrieves channel records matching filters. **Gotcha**: Filters out channels whose `UserChannelName` ends with `DFConstantsAndEnums.USER_CHANNEL_NAME_HUMIDITY` (see *Gotchas*).
- **`ulong ChannelsDelete(IUserDbRecord user, IConnectionDetails connection, long id, out string errorString)`**
Deletes a channel by `id`. Returns error code and populates `errorString` (from stored procedure or exception) on failure.
## 3. Invariants
- **Authentication**: All methods require the user to be logged in (`IsUserLoggedIn(user, connection)`). Returns `ERROR_ACCESS_DENIED` otherwise.
- **Parameter Validation**:
- `ChannelCodesInsert`/`ChannelCodesUpdate`: `channelCode.Code` and `channelCode.Name` must be non-null/non-empty.
- `ChannelCodesGet`: `channelTypeLookup` must be non-null.
- `ChannelsInsert`: `channel.Id` is output-only; input `Id` is ignored (but `GroupId`, `DASId`, `DASChannelIndex`, etc., must be set appropriately).
- **Null Handling**:
- Optional filter parameters (e.g., `id`, `code`, `name`, `codeType`) are passed as SQL `NULL` if null.
- Nullable DB columns (`DASId`, `SensorId`) are omitted from command parameters if ≤ 0.
- **Error Reporting**:
- Stored procedures return `@errorNumber` and `@errorMessage`. Non-zero `@errorNumber``ERROR_UNKNOWN`.
- Exceptions during execution are logged and return `ERROR_UNKNOWN`.
- **Resource Cleanup**: All methods dispose `SqlCommand` and connection in `finally` blocks.
## 4. Dependencies
### Imports/Usings (from source)
- **Core**: `System`, `System.Data`, `System.Data.SqlClient`, `System.Diagnostics`
- **DbAPI**: `DbAPI.Connections`, `DbAPI.Errors`, `DbAPI.Logging`
- **Common Types**:
- `DTS.Common.Interface.Database` (`IUserDbRecord`, `IConnectionDetails`)
- `DTS.Common.Interface.Channels` (`IChannelCode`, `IChannelSettingRecord`, `IGroupChannelSettingRecord`, `IChannelDbRecord`)
- `DTS.Common.Classes.Channels`, `DTS.Common.Classes.ChannelCodes`, `DTS.Common.Classes.Groups.ChannelSettings`
- `DTS.Common.Enums.Channels` (`ChannelEnumsAndConstants`, `ChannelCodeType`)
- `DTS.Common` (`Constants`, `Utility`, `DFConstantsAndEnums`)
### Inferred Dependencies
- **Database**:
- Stored procedures: `sp_ChannelCodesInsert`, `sp_ChannelCodesUpdate`, `sp_ChannelCodesDelete`, `sp_ChannelCodeTypeGet`, `sp_ChannelCodesGet`, `sp_ChannelSettingsUpdate`, `sp_ChannelSettingsGet`, `sp_GroupChannelSettingsDelete`, `sp_GroupChannelSettingsInsert`, `sp_GroupChannelSettingsGet`, `sp_ChannelsInsert`, `sp_ChannelsUpdate`, `sp_ChannelsGet`, `sp_ChannelsDelete`.
- User-defined table type: `dbo.StringList` (used in `GroupChannelSettingsGet`).
- **Other Modules**:
- `ConnectionManager` (for `GetSqlCommand`)
- `Database.Database` (for `GetStoredProcedureVersionCached`, `PrepareForDbAccess`)
- `LogManager` (for logging)
- `ErrorCodes` (for return values)
- `Utility` (for `GetShort`, `GetString`)
- `DFConstantsAndEnums` (for `USER_CHANNEL_NAME_HUMIDITY` constant)
### Implemented By
- `Channels` (internal class in same namespace)
## 5. Gotchas
- **`ChannelsGet` filters out humidity channels**: The implementation explicitly skips records where `UserChannelName.EndsWith(DFConstantsAndEnums.USER_CHANNEL_NAME_HUMIDITY)`. This is hardcoded (comment references case #33192). Consumers cannot override this behavior.
- **`GroupChannelSettingsGet` uses two different stored procedures**: Behavior changes based on `clientDbVersion` relative to `Constants.BULK_GROUPCHANNELSETTINGS_GET_DB_VERSION`. The older version loops over channels and reuses the same `SqlCommand` (parameters cleared between iterations), while the newer uses a table-valued parameter. This implies version compatibility must be handled by callers.
- **`ChannelsInsert` mutates input**: The `channel` parameter is `ref`, and `channel.Id` is updated with the new database ID on success. Callers must pass a mutable instance.
- **Nullable DB columns handled inconsistently**:
- In `ChannelsInsert`, `DASId`/`SensorId` are only added to the command if `> 0`.
- In `ChannelsUpdate`, `DASId` is only added if `> 0`, but `DASChannelIndex` is *always* added (even if `0`).
This may cause issues if `0` is a valid value for `DASChannelIndex` but not for `DASId`.
- **`ChannelSettingsUpdate` uses `ExecuteReader()` instead of `ExecuteNonQuery()`**: Despite updating a setting (not returning rows), it calls `ExecuteReader()` and then discards the reader. This is unusual and may be legacy behavior.
- **`ChannelCodesInsert` logs to wrong event**: Error logs use `LogManager.LogEvents.TestSetups` instead of a channel-specific event. Likely copy-paste error.
- **No validation on `channelCode.CodeType`**: The `lookup` dictionary is accessed directly (`lookup[channelCode.CodeType]`) without checking if the key exists. Will throw `KeyNotFoundException` if `channelCode.CodeType` is not in `lookup`.

View File

@@ -0,0 +1,187 @@
---
source_files:
- DataPRO/DbAPI/Connections/IConnectionDetails.cs
- DataPRO/DbAPI/Connections/ConnectionDetails.cs
- DataPRO/DbAPI/Connections/IConnections.cs
- DataPRO/DbAPI/Connections/ConnectionManager.cs
generated_at: "2026-04-16T04:26:39.468072+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "ce0c80cfa1afa7c7"
---
# Documentation: Connection Management Module
## 1. Purpose
This module provides core database connection and user authentication functionality for the DataPRO system, abstracting both remote SQL Server connections and local SQL Server LocalDB instance management. It defines interfaces and implementations for establishing database connections (including dynamic LocalDB instance lifecycle management), authenticating users via password or pre-hashed credentials, and tracking active connections and logged-in users. The module serves as the foundational layer for all database interactions in the system, ensuring consistent connection handling, user session tracking, and error reporting.
## 2. Public Interface
### `IConnectionDetails` Interface
- **`bool UseNTLMAuthentication { get; set; }`**
Indicates whether to use Windows Authentication (NTLM) instead of SQL authentication. Defaults to `true`.
- **`string DbUser { get; set; }`**
Username for SQL authentication. Defaults to `"DataPROUser"`.
- **`string DbUserPassword { get; set; }`**
Password for SQL authentication. Defaults to `"DTSSealBeachHQ"`.
- **`string InstanceName { get; set; }`**
Name of the LocalDB instance to use. Defaults to `"DataPROInstance"`.
- **`string DbServer { get; set; }`**
Server name for remote DB connections. Defaults to `@"(localdb)\DataPROInstance"`.
- **`string DbName { get; set; }`**
Name of the database to connect to. Defaults to `"DataPRO"`.
- **`bool UsingCentralizedDb { get; set; }`**
`true` if connecting to a centralized (remote) database; `false` for LocalDB.
- **`string DbFolderPath { get; set; }`**
Path to directory containing `.mdf` and `.ldf` files for LocalDB attachment.
- **`string AttachDbsBatPath { get; set; }`**
Full path to `attach.bat` script used to attach databases via `sqlcmd.exe`.
- **`string ODBCToolsPath { get; set; }`**
Path to directory containing `sqlcmd.exe`.
- **`string SqlDbPath { get; set; }`**
Path to directory containing `SqlLocalDB.exe`.
- **`int ClientDbVersion { get; set; }`**
Database version expected by the client (code-defined). Must be initialized by clients.
- **`int ConnectionDbVersion { get; set; }`**
Database version actually in use (e.g., from metadata). Must be initialized by clients.
- **`string GetConnectionString()`**
Returns a connection string for `SqlClient`. Uses Windows Auth if `UseNTLMAuthentication` is `true`; otherwise uses SQL auth with `DbUser`/`DbUserPassword`. Caches result in internal `_Connection` field.
- **`IConnectionDetails Clone()`**
Returns a deep copy of the current instance.
### `ConnectionDetails` Class
- **`ConnectionDetails()`**
Default constructor. Initializes all properties to their default values.
- **`ConnectionDetails(ConnectionDetails details)`**
Copy constructor. Performs deep copy of all fields, including `_Connection`.
- **`string GetConnectionString()`**
Overrides interface method. Returns cached or newly generated connection string.
- **`string ToString()`**
Returns `"Server\DbName"` if `UsingCentralizedDb` is `true`; otherwise `"Local\DbName"`.
### `IConnections` Interface
- **`IConnectionDetails[] GetActiveConnections()`**
Returns array of all currently active `IConnectionDetails` instances.
- **`ulong LoginUserHash(IConnectionDetails connection, string user, string hash, out IUserDbRecord userObject)`**
Authenticates user using a pre-hashed password (SHA256 base64-encoded). Returns `ERROR_SUCCESS` on success, `ERROR_LOGINFAILED` if hash mismatch, `ERROR_UNKNOWN` on exception.
- **`ulong LoginUser(IConnectionDetails connection, string user, string password, out IUserDbRecord userObject)`**
Authenticates user using plaintext password. Computes SHA256 hash of `"{password}_{user}"`, base64-encodes it, and compares to stored hash. Returns same error codes as `LoginUserHash`.
- **`ulong ConnectToDb(IConnectionDetails details)`**
Establishes database connection. For LocalDB (`UsingCentralizedDb == false`), manages instance lifecycle: stops/deletes/creates/starts instance, then attaches databases. Returns `ERROR_SUCCESS` on success, `ERROR_UNKNOWN` on failure.
- **`bool IsUserLoggedIn(IUserDbRecord user, IConnectionDetails connection)`**
Returns `true` if `user` is logged in to `connection`.
- **`Tuple<IUserDbRecord, IConnectionDetails>[] GetLoggedInUsers()`**
Returns array of all logged-in user/connection pairs.
- **`void ClearConnections()`**
Removes all logged-in users and all active connections.
### `ConnectionManager` Class *(Internal)*
- **`ulong ConnectToDb(IConnectionDetails details)`**
Public entry point for `IConnections.ConnectToDb`. Wraps `Connect()` in logging and exception handling.
- **`IConnectionDetails[] GetActiveConnections()`**
Returns snapshot of active connections.
- **`ulong LoginUserHash(...)` / `ulong LoginUser(...)`**
Implement `IConnections` methods. `LoginUser` computes SHA256 hash of `"{password}_{user}"` (UTF-8 encoded) and compares to `foundUser.Password`.
- **`bool IsUserLoggedIn(...)`**
Checks `_loggedInUsers` list for matching user/connection pair.
- **`Tuple<IUserDbRecord, IConnectionDetails>[] GetLoggedInUsers()`**
Returns snapshot of `_loggedInUsers`.
- **`void ClearConnections()`**
Clears `_loggedInUsers` and `_connections` lists.
- **`private ulong Connect(IConnectionDetails details)`**
Core connection logic: routes to `ConnectRemote` or `ConnectLocal`, clones details, and adds to `_connections` on success.
- **`private ulong ConnectLocal(IConnectionDetails details)`**
Manages LocalDB instance lifecycle:
1. Stops existing instance (ignores "instance doesnt exist" errors)
2. Deletes existing instance (ignores "instance doesnt exist" errors)
3. Creates new instance
4. Starts instance
5. Attaches databases (`DataPRO` and `ISO`)
Returns `ERROR_SUCCESS` or `ERROR_UNKNOWN`.
- **`private void AttachDatabases(IConnectionDetails details, LogDelegate log)`**
Calls `AttachDatabase` for `details.DbName` and `"ISO"`.
- **`private static void AttachDatabase(...)`**
Executes `attach.bat` script with parameters: `dbName`, `dbFileName`, `logFileName`, `fullSqlcmdPath`. Throws `Exception` on non-empty result.
- **`internal static ulong GetSqlCommand(IConnectionDetails con, out SqlCommand cmd, string commandText = "")`**
Helper to create/open `SqlCommand` using `con.GetConnectionString()`. Returns `ERROR_SUCCESS` or `ERROR_UNKNOWN`.
## 3. Invariants
- **Connection String Caching**: `GetConnectionString()` caches the result in `_Connection` and reuses it on subsequent calls.
- **LocalDB Instance Lifecycle**: For LocalDB connections, `ConnectLocal` *always* stops, deletes, creates, and starts the instance—regardless of prior state—except when "instance doesnt exist" errors occur (which are silently ignored).
- **User Authentication Hashing**: Passwords are hashed as `SHA256(UTF8("{password}_{user}"))` and stored/compared as base64 strings.
- **Thread Safety**: `_connections` and `_loggedInUsers` are accessed under `CONNECT_LOCK` and `LOGIN_LOCK` respectively. `ProcessSqlLocalDbCommand` and `BatchCommandProcessor` use `PROCESS_LOCK`.
- **Database Attachment**: Only two databases are attached: `details.DbName` and `"ISO"`.
- **Error Codes**:
- `ERROR_SUCCESS` (0) = success
- `ERROR_LOGINFAILED` = authentication failure
- `ERROR_UNKNOWN` = unexpected error
- `ERROR_MISSING_PARAMETER` = null/empty `user` or `connection` in `LoginUser`
## 4. Dependencies
### Dependencies *on* this module:
- **`DbAPI.User.User`**: Used in `LoginUser`/`LoginUserHash` to retrieve user records (`User.GetUser`).
- **`DTS.Common.Interface.Database.IUserDbRecord`**: Interface for user records used in login/session tracking.
- **`DTS.Common.Utils.Database.SqlServerLocalDbException`**: Custom exception type used to signal LocalDB-specific errors (e.g., instance not found).
- **`DTS.Common.Utils.Database.LogDelegate`**: Logging delegate used in LocalDB command execution.
- **`DbAPI.Errors.ErrorCodes`**: Defines error constants (`ERROR_SUCCESS`, `ERROR_LOGINFAILED`, etc.).
- **`DbAPI.Logging.LogManager`**: Used for logging connection/login events.
### Dependencies *of* this module:
- **`System.Data.SqlClient`**: Used for `SqlConnection`/`SqlCommand`.
- **`System.Diagnostics`**: Used for `Process` management (LocalDB/attach.bat).
- **`System.IO`**: Used for file operations (`Path.Combine`, `FileInfo`).
- **`System.Security.Cryptography`**: Used for `SHA256` hashing in `LoginUser`.
- **`DTS.Common.Utilities.Logging.APILogger`**: Used as logging target in `ConnectLocal`.
## 5. Gotchas
- **Hardcoded Credentials**: Default `DbUser` and `DbUserPassword` are hardcoded (`"DataPROUser"` / `"DTSSealBeachHQ"`). These should be overridden in production.
- **SHA256 Hash Format**: Password hashing uses `SHA256("{password}_{user}")`—*not* standard salted hashing. The user is appended to the salted password string, which is non-standard and may be a security risk.
- **LocalDB Instance Destruction**: `ConnectLocal` *destroys* the LocalDB instance on every connection attempt. This may cause data loss if `.mdf`/`.ldf` files are not persisted or backed up.
- **Missing Validation**: `ConnectionDetails` does not validate required fields (e.g., `DbFolderPath`, `AttachDbsBatPath`, `ODBCToolsPath`, `SqlDbPath`) before attempting LocalDB operations. `ConnectLocal` may fail with cryptic errors if these are empty.
- **`GetSqlCommand` Opens Connection**: `GetSqlCommand` opens the SQL connection but does *not* dispose it. Callers must manage connection lifetime.
- **`Clone()` Shallow Copy of `_Connection`**: The copy constructor copies `_Connection` directly, but since its a string (immutable), this is safe. However, if `_Connection` were mutable, this could be a bug.
- **`ConnectRemote` Does Nothing**: `ConnectRemote` currently returns `ERROR_SUCCESS` without performing any actual connection logic. Remote DB connections are effectively unimplemented.
- **`ProcessSqlLocalDbCommand` Uses Global `StringBuilder`**: Static `sb`/`sbErrors` are used in `SqlCommandProcessor` and `BatchCommandProcessor`. While protected by `PROCESS_LOCK`, this design is fragile and not thread-safe across *other* callers of these methods.
- **`AttachDatabase` Ignores Missing `sqlcmd.exe`**: Logs a warning if `sqlcmd.exe` is missing but proceeds to execute the batch file anyway, likely causing failure.
- **No Timeout Handling**: No explicit timeout configuration for `Process.WaitForExit()` or SQL connections—could hang indefinitely on hung operations.

View File

@@ -0,0 +1,120 @@
---
source_files:
- DataPRO/DbAPI/DAS/IDataRecorders.cs
- DataPRO/DbAPI/DAS/DataRecorders.cs
generated_at: "2026-04-16T04:25:47.865565+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "6d8f47b7e121e9aa"
---
# Documentation: `IDataRecorders` Interface and `DataRecorders` Implementation
## 1. Purpose
This module provides data access service (DAS) functions for managing data recorders (DAS units) and their associated channels in the database. It serves as the persistence layer for DAS-related operations—including CRUD (Create, Read, Update, Delete), channel management, and hierarchical relationships (e.g., parent/child encapsulated DAS). The interface `IDataRecorders` defines the contract, while the internal class `DataRecorders` implements it using SQL Server stored procedures and ADO.NET. It enforces user authentication, logs errors, and ensures database version compatibility for certain DAS types (e.g., SLICE6_AIR_TC).
## 2. Public Interface
The interface `IDataRecorders` defines the following public methods:
### `ulong DASChannelsDelete(IUserDbRecord user, IConnectionDetails connection, string hardwareId)`
- **Behavior**: Deletes all DAS channel records associated with the given `hardwareId` (format: `SerialNumber_DASType`).
- **Returns**: `ERROR_SUCCESS` (0) on success; otherwise, an error code (e.g., `ERROR_ACCESS_DENIED`, `ERROR_UNKNOWN`).
- **Note**: Does *not* delete the DAS record itself—only its channels.
### `ulong DASChannelsInsert(IUserDbRecord user, IConnectionDetails connection, string hardwareId, ref IDASChannelDBRecord record)`
- **Behavior**: Inserts a new DAS channel record into the database. On success, populates `record.DaschannelId` with the new database ID.
- **Returns**: `ERROR_SUCCESS` (0) on success; `ERROR_MISSING_PARAMETER` if `record` or `hardwareId` is null/empty; `ERROR_ACCESS_DENIED` if user not logged in; `ERROR_UNKNOWN` on failure.
- **Note**: Modifies the `record` parameter *by reference* to update the `DaschannelId`.
### `ulong DASChannelsGet(IUserDbRecord user, IConnectionDetails connection, string hardwareId, out IDASChannelDBRecord[] channels)`
- **Behavior**: Retrieves all DAS channel records for the given `hardwareId`. If `hardwareId` is `null`, returns *all* DAS channel records.
- **Returns**: `ERROR_SUCCESS` (0) on success; `ERROR_ACCESS_DENIED` if user not logged in; `ERROR_UNKNOWN` on failure.
- **Output**: `channels` is always initialized to an array (possibly empty) on return.
### `ulong DASChildrenGet(IUserDbRecord user, IConnectionDetails connection, string dasSerialNumber, out string[] childrenSerialNumbers)`
- **Behavior**: Retrieves serial numbers of all child DAS units associated with the given parent `dasSerialNumber`. Used for encapsulated/compacted DAS discovery.
- **Returns**: `ERROR_SUCCESS` (0) on success; `ERROR_ACCESS_DENIED` if user not logged in; `ERROR_UNKNOWN` on failure.
- **Output**: `childrenSerialNumbers` is always initialized to an array (possibly empty) on return.
### `ulong DASDelete(IUserDbRecord user, IConnectionDetails connection, int DASId, string serialNumber, bool embedded)`
- **Behavior**: Deletes a DAS record from the database. Requires *either* `DASId > 0` *or* non-null/non-empty `serialNumber`.
- Removes associated test setup entries.
- Removes channel assignments (and channels if not `embedded`).
- Deletes DAS channels and metadata.
- **Returns**: `ERROR_SUCCESS` (0) on success; `ERROR_MISSING_PARAMETER` if both `DASId ≤ 0` and `serialNumber` is null/empty; `ERROR_ACCESS_DENIED` if user not logged in; `ERROR_UNKNOWN` on failure.
### `ulong DASGet(IUserDbRecord user, IConnectionDetails connection, int clientDbVersion, string DASSerial, string position, out IDASDBRecord[] das)`
- **Behavior**: Retrieves DAS records matching the search criteria.
- If both `DASSerial` and `DASId` are omitted (via `null`/empty), returns *all* DAS records.
- Does *not* enforce user permission checks (only login status).
- **Returns**: `ERROR_SUCCESS` (0) on success; `ERROR_ACCESS_DENIED` if user not logged in; `ERROR_UNKNOWN` on failure.
- **Output**: `das` may be `null` or an empty array on success.
### `ulong DASInsert(IUserDbRecord user, IConnectionDetails connection, IDASDBRecord das)`
- **Behavior**: Inserts a new DAS record into the database. On success, populates `das.DASId` with the new database ID.
- **Returns**: `ERROR_SUCCESS` (0) on success; `ERROR_MISSING_PARAMETER` if `das` is `null`; `ERROR_ACCESS_DENIED` if user not logged in; `ERROR_UNKNOWN` on failure or version mismatch.
- **Special Behavior**: For `DASType == SLICE6_AIR_TC`, verifies `serverDbVersion >= SLICE_TC_DB_VERSION`; otherwise, returns `ERROR_UNKNOWN`.
### `ulong DASUpdate(IUserDbRecord user, IConnectionDetails connection, IDASDBRecord das)`
- **Behavior**: Updates an existing DAS record in the database.
- **Returns**: `ERROR_SUCCESS` (0) on success; `ERROR_ACCESS_DENIED` if user not logged in; `ERROR_UNKNOWN` on failure.
- **Note**: Does *not* validate presence of `DASId`; relies on stored procedure logic to locate the record.
---
## 3. Invariants
- **User Authentication**: All methods require the user to be logged in (checked via `IsUserLoggedIn(user, connection)`). Failure returns `ERROR_ACCESS_DENIED`.
- **Parameter Validation**:
- `DASChannelsInsert`: Fails early if `record == null` or `hardwareId` is null/empty/whitespace.
- `DASDelete`: Requires at least one of `DASId > 0` or non-empty `serialNumber`.
- `DASInsert`: Fails early if `das == null`.
- **Database Version Compatibility**: For `SLICE6_AIR_TC` DAS types, insertion is blocked if `serverDbVersion < SLICE_TC_DB_VERSION`.
- **Output Initialization**: All `out` array parameters (`channels`, `childrenSerialNumbers`, `das`) are initialized to a non-null array (possibly empty) before returning.
- **Resource Cleanup**: All database connections (`cmd.Connection`) are disposed in `finally` blocks.
- **Error Reporting**: SQL stored procedure errors are logged via `LogManager`, and the method returns `ERROR_UNKNOWN` (not the raw DB error code).
---
## 4. Dependencies
### Module Dependencies
- **Imports/Usings**:
- `DbAPI.Connections` (for `IsUserLoggedIn`, `ConnectionManager`, `GetDatabaseVersion`)
- `DbAPI.Errors` (for `ErrorCodes`)
- `DbAPI.Logging` (for `LogManager`)
- `DTS.Common.Interface.DataRecorders` (for `IDASChannelDBRecord`, `IDASDBRecord`)
- `DTS.Common.Interface.Database` (for `IUserDbRecord`, `IConnectionDetails`)
- `DTS.Common.Classes`, `DTS.Common.Enums.*` (for types like `HardwareTypes`, `DFConstantsAndEnums`)
- `System.Data`, `System.Data.SqlClient` (ADO.NET types)
### External Dependencies
- **Database**: Requires SQL Server with the following stored procedures:
- `sp_DASChannelsDelete`
- `sp_DASChannelsInsert`
- `sp_DASChannelsGet`
- `sp_DASChildrenGet`
- `sp_DASDelete`
- `sp_DASGet`
- `sp_DASInsert`
- `sp_DASUpdate`
- **Database Version**: Must support `SLICE_TC_DB_VERSION` constant for `SLICE6_AIR_TC` DAS types.
### Depended Upon
- **Consumers**: Other modules in `DbAPI.DAS` (e.g., higher-level services or UI layers) that need DAS CRUD operations.
---
## 5. Gotchas
- **Parameter Name Typo**: In `DASChannelsGet`, the parameter `connetion` (missing 'c') is used instead of `connection`. This is consistent in both interface and implementation.
- **`DASUpdate` Does Not Validate `DASId`**: Unlike typical updates, `DASUpdate` does not require `das.DASId` to be set; it relies on the stored procedure to locate the record (likely via `SerialNumber` or other fields). This could cause unintended updates if `SerialNumber` is not unique or stable.
- **`DASInsert` Modifies Input Record**: The `das` parameter is mutated to include the new `DASId` on success. Callers must be aware of this side effect.
- **`DASChannelsInsert` Modifies Input Record**: Similarly, `record` is mutated to include `DaschannelId`.
- **`DASGet` Ignores `DASId` Parameter**: The interface declares `int DASId` but the implementation does *not* use it (see `DASGet` signature in `IDataRecorders.cs`). Only `DASSerial` and `position` are used. This is likely a legacy or incomplete parameter.
- **`DASDelete` Behavior with `embedded` Flag**: When `embedded == true`, channels are *not* deleted (only assignments are removed). When `embedded == false`, channels *are* deleted. This is critical for correct usage.
- **`DASInsert` Version Check is Hardcoded**: The version check for `SLICE6_AIR_TC` is hardcoded and does not use a configurable constant beyond `DTS.Common.Constants.SLICE_TC_DB_VERSION`. Ensure this constant is up-to-date.
- **No Transaction Handling**: Operations are executed individually (no explicit transaction scope), risking partial failures if multiple operations must succeed/fail together.
- **`Console.WriteLine` in Production Code**: `DataRecorders.cs` contains `Console.WriteLine` calls (e.g., in `DASDelete`), which may leak debug output in production.

View File

@@ -0,0 +1,126 @@
---
source_files:
- DataPRO/DbAPI/Database/IDatabase.cs
- DataPRO/DbAPI/Database/Database.cs
generated_at: "2026-04-16T04:24:57.920635+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "460fbd3d82516ff4"
---
# Database
## Documentation: `DbAPI.Database` Module
---
### 1. Purpose
This module provides core database versioning and metadata operations for the DataPRO system. It defines and implements the `IDatabase` interface, enabling retrieval of the highest database version (`GetDatabaseVersion`), insertion of new version records (`InsertDatabaseVersion`), and extraction of the SQL Server major version (`GetSQLVersion`). It also includes utility methods for resolving versioned stored procedure names (`GetStoredProcedureVersion`, `GetStoredProcedureVersionCached`, `PrepareForDbAccess`), supporting backward/forward compatibility across database schema versions. The module acts as a low-level abstraction layer between application logic and SQL Server, enforcing user authentication and delegating to stored procedures for persistence.
---
### 2. Public Interface
The interface `IDatabase` is the public contract. The concrete implementation `Database` is `internal`, so only `IDatabase` members are externally visible.
#### `IDatabase.GetSQLVersion(IConnectionDetails connection) → int`
- **Behavior**: Retrieves the major version number (e.g., `15` for SQL Server 2019) from the servers `ServerVersion` string.
- **Notes**: Does *not* require user authentication. Returns `0` on failure (e.g., parsing error or connection issue).
#### `IDatabase.GetDatabaseVersion(IUserDbRecord user, IConnectionDetails connection, out int version) → ulong`
- **Behavior**: Executes the stored procedure `sp_DBVersionGet` to fetch all version records, then returns the maximum version number via the `out int version` parameter.
- **Authentication**: Does *not* enforce user login (despite XML comment stating “User must be logged in” — implementation explicitly allows querying without login).
- **Return**: `ErrorCodes.ERROR_SUCCESS` (0) on success; `ErrorCodes.ERROR_UNKNOWN` on exception.
- **Output**: `version` is set to `0` on failure.
#### `IDatabase.InsertDatabaseVersion(IUserDbRecord user, IConnectionDetails connection, int version, int step, DateTime date, string remarks, string userField) → ulong`
- **Behavior**: Inserts a new version record into the database by calling the stored procedure `sp_DbVersionInsert`.
- **Authentication**: Explicitly checks `IsUserLoggedIn(user, connection)`; returns `ErrorCodes.ERROR_ACCESS_DENIED` if not logged in.
- **Parameters**:
- `version`, `step`, `date`, `remarks`, `userField` → values inserted into the record.
- **Return**: `ErrorCodes.ERROR_SUCCESS` on success; `ErrorCodes.ERROR_UNKNOWN` on SQL error (non-zero `@errorNumber` output) or exception.
- **Notes**: Does *not* validate uniqueness or ordering of version numbers — duplicates or regressions are allowed.
#### `Database.GetStoredProcedureVersion(IConnectionDetails connection, string storedProcedure, int clientDbVersion) → string`
- **Behavior**: Resolves the correct stored procedure name (e.g., `sp_MyProc` vs `sp_MyProc_3`) based on client and DB versions. Calls `DbAPI.GetStoredProcedureToUse`.
- **Fallback**: Returns original `storedProcedure` name if resolution fails.
#### `Database.GetStoredProcedureVersionCached(IConnectionDetails connection, string storedProcedure) → string`
- **Behavior**: Same as `GetStoredProcedureVersion`, but uses a cached result (via `DbAPI.GetStoredProcedureToUseCached`) to avoid repeated DB lookups.
- **Uses**: `connection.ClientDbVersion` as the client version.
#### `Database.PrepareForDbAccess(IUserDbRecord user, IConnectionDetails connection, int clientDbVersion, string storedProcedure, out int storedProcedureVersionToUse, out SqlCommand cmd) → ulong`
- **Behavior**: A helper to prepare a `SqlCommand` for a versioned stored procedure.
- **Steps**:
1. Checks user login (`IsUserLoggedIn`) → returns `ERROR_ACCESS_DENIED` if false.
2. Resolves stored procedure name version via `DbAPI.GetStoredProcedureToUse`.
3. Creates `SqlCommand` via `ConnectionManager.GetSqlCommand`.
- **Output**:
- `storedProcedureVersionToUse`: resolved version suffix (e.g., `3`), or `0` if none.
- `cmd`: fully configured `SqlCommand` object.
- **Return**: `ERROR_SUCCESS` on success; otherwise error code (e.g., `ERROR_ACCESS_DENIED`, `ERROR_UNKNOWN`).
---
### 3. Invariants
- **Authentication Enforcement**: `InsertDatabaseVersion` and `PrepareForDbAccess` *require* the user to be logged in (checked via `IsUserLoggedIn`). Failure to meet this yields `ERROR_ACCESS_DENIED`.
- **Stored Procedure Naming**: Versioned stored procedures follow the pattern `{baseName}_{version}` (e.g., `sp_MyProc_2`). A version suffix of `0` implies no suffix (i.e., base name only).
- **Error Handling**: All methods log exceptions to `LogManager` at `TraceEventType.Error` level before returning `ERROR_UNKNOWN`.
- **Resource Management**: All `SqlCommand` objects created via `ConnectionManager.GetSqlCommand` have their `.Connection` disposed in `finally` blocks.
- **No Validation on Version Values**: `InsertDatabaseVersion` does *not* check for duplicate versions, ordering (e.g., regression), or consistency with existing records.
---
### 4. Dependencies
#### Imports/References:
- `DbAPI.Connections` → Provides `ConnectionManager`, `IsUserLoggedIn`, and `GetStoredProcedureToUse`/`GetStoredProcedureToUseCached`.
- `DbAPI.Errors` → Defines `ErrorCodes` (e.g., `ERROR_SUCCESS`, `ERROR_ACCESS_DENIED`, `ERROR_UNKNOWN`).
- `DbAPI.Logging``LogManager` for error logging.
- `DTS.Common.Interface.Database` → Interfaces `IUserDbRecord`, `IConnectionDetails`.
- `DTS.Common.Classes` → Likely contains `Utility.GetInt` (used in `GetDatabaseVersion`).
- `System.Data`, `System.Data.SqlClient` → Core ADO.NET types (`SqlCommand`, `SqlParameter`, `SqlDbType`, `CommandType`, `DBNull`, `IDataReader`).
#### Used By:
- Any module requiring database versioning operations (e.g., upgrade scripts, version reporting, audit logging).
- Likely consumed via `IDatabase` interface (injected or resolved via DI/container — not evident from source).
---
### 5. Gotchas
- **Contradictory Authentication Requirement**:
`GetDatabaseVersion`s XML comment states *“User must be logged into database”*, but the implementation does **not** call `IsUserLoggedIn`. This is a discrepancy between documentation and behavior.
- **No Version Uniqueness/Ordering Checks**:
`InsertDatabaseVersion` blindly inserts records — callers must ensure version numbers are monotonically increasing and unique, or risk data inconsistency.
- **`GetSQLVersion` Fragility**:
Assumes `ServerVersion` is dot-separated (e.g., `"15.0.2000.5"`). Parsing relies on `Split(".")` and `int.Parse` on the first segment — will throw if format changes (e.g., `"15-0-2000"` or `"15.0.2000.5-SP2"`).
- **`GetDatabaseVersion` Uses `Max()` on `List<int>`**:
If no version records exist, `dbVersionsList.Max()` throws `InvalidOperationException`. However, the `catch` block suppresses this and returns `ERROR_UNKNOWN` with `version = 0`. This is safe but may mask empty-db scenarios.
- **Stored Procedure Name Resolution**:
`GetStoredProcedureVersion` and `PrepareForDbAccess` rely on external `DbAPI.GetStoredProcedureToUse`/`GetStoredProcedureToUseCached`. Behavior of these (e.g., how version mapping is determined) is not visible here.
- **`userField` Parameter Misleading Name**:
The `userField` parameter in `InsertDatabaseVersion` is documented as *“What user to associate with the insert record (does not have to match any actual users)”* — implying its a free-text field, not a foreign key. Do not assume it validates against a user table.
- **No Transaction Support**:
All operations are standalone; no mechanism for atomic multi-step versioning (e.g., insert + update).
- **`GetStoredProcedureVersionCached` Uses `connection.ClientDbVersion`**:
Assumes `IConnectionDetails` exposes a `ClientDbVersion` property — not visible in this module but required for correct behavior.
---
*Documentation generated from provided source files. No external behavior or APIs inferred beyond what is explicitly present.*

View File

@@ -0,0 +1,57 @@
---
source_files:
- DataPRO/DbAPI/Errors/ErrorCodes.cs
generated_at: "2026-04-16T04:26:28.421504+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "0b2f1f90c61e6679"
---
# Errors
## 1. Purpose
This module defines a centralized set of error codes and a string conversion utility for the `DbAPI` layer of the proprietary codebase. It serves as a canonical source of error constants (e.g., `ERROR_ACCESS_DENIED`, `INVALID_SESSIONID`) used throughout database-related operations, enabling consistent error reporting and handling. The `ResultToString` method maps numeric error codes to human-readable string representations, facilitating debugging and logging.
## 2. Public Interface
- **`public const ulong ERROR_SUCCESS = 0x0`**
Indicates successful operation.
- **`public const ulong ERROR_INVALID_FUNCTION = 0x1`**
Generic error for an invalid or unsupported function call.
- **`public const ulong ERROR_FILE_NOT_FOUND = 0x2`**
Indicates a requested file could not be located.
- **`public const ulong ERROR_PATH_NOT_FOUND = 0x3`**
Indicates a requested path (directory) could not be located.
- **`public const ulong ERROR_ACCESS_DENIED = 0x5`**
Indicates insufficient permissions to perform the requested operation.
- **`public const ulong ERROR_NOT_SUPPORTED = 0x32`**
Indicates the requested operation is not supported.
- **`public const ulong INVALID_SESSIONID = 0x200`**
Indicates an invalid or expired session identifier was provided.
- **`public const ulong ERROR_UNKNOWN = 0x201`**
Fallback error code for unrecognized or unclassified errors.
- **`public const ulong ERROR_NOUSER = 0x202`**
Indicates no user context is available (e.g., unauthenticated or deleted user).
- **`public const ulong ERROR_LOGINFAILED = 0x203`**
Indicates authentication failure (e.g., invalid credentials).
- **`public const ulong ERROR_MISSING_PARAMETER = 0x204`**
Indicates a required parameter was omitted in a function call.
- **`public static string ResultToString(ulong hr)`**
Converts a numeric error code (`hr`) to a descriptive string (e.g., `"ACCESS_DENIED"`). For unrecognized codes, returns the hex representation (e.g., `"205"` for `0x205`).
## 3. Invariants
- All error codes are `const ulong` and assigned explicit hexadecimal values.
- `ResultToString` is deterministic: the same input `hr` always produces the same output string.
- The `switch` statement in `ResultToString` is exhaustive for all defined constants; any value not explicitly handled falls back to hex formatting.
- No mutable state exists in this class (it is `abstract` and contains only constants and a static method).
## 4. Dependencies
- **Internal dependencies**: None (no external references in the provided source).
- **Consumers**: This module is expected to be referenced by other components in the `DbAPI` layer (e.g., database connection, query execution, or session management modules) to standardize error reporting.
- **No external libraries or frameworks** are imported or referenced in this file.
## 5. Gotchas
- The `abstract` modifier on `ErrorCodes` is unusual for a pure constants class (typically `static class` is used in C# for this pattern); this may imply intentional extensibility (e.g., subclassing for environment-specific codes), but no subclasses are present in the provided source.
- The `default` case in `ResultToString` returns `hr.ToString("X")`, which omits the `0x` prefix (e.g., `"205"` instead of `"0x205"`), potentially reducing clarity in logs.
- The constant `ERROR_LOGINFAILED` uses a string literal `"LOGIN_FAILED"` instead of `@"LOGIN_FAILED"` (consistent verbatim string usage elsewhere suggests this may be unintentional).
- No error codes between `0x205` and `0x201` (e.g., `0x205``0x201` is invalid range; likely meant `0x205``0x209`) are defined, suggesting possible gaps or future expansion points.
- None identified from source alone.

View File

@@ -0,0 +1,102 @@
---
source_files:
- DataPRO/DbAPI/Groups/IGroupHardware.cs
- DataPRO/DbAPI/Groups/IGroups.cs
- DataPRO/DbAPI/Groups/GroupHardware.cs
- DataPRO/DbAPI/Groups/Groups.cs
generated_at: "2026-04-16T04:26:08.555940+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "1e70835e7b48ab2d"
---
# GroupHardware and Groups Module Documentation
---
## 1. Purpose
This module provides data access layer (DAL) functionality for managing *Groups* and *GroupHardware* entities in the database. It exposes interfaces (`IGroups`, `IGroupHardware`) and their concrete implementations (`Groups`, `GroupHardware`) to perform CRUD operations (Create, Read, Update, Delete) against the `Groups` and `GroupHardware` tables via stored procedures. The module enforces user authentication before executing database operations and centralizes error handling and logging for these operations. It serves as the primary interface for group-based configuration and hardware association logic elsewhere in the system.
---
## 2. Public Interface
### `IGroupHardware` Interface (Namespace: `DbAPI.GroupHardware`)
| Method | Signature | Description |
|--------|-----------|-------------|
| `GroupHardwareInsert` | `ulong GroupHardwareInsert(IUserDbRecord user, IConnectionDetails connection, GroupHardwareDbRecord groupHardware, out int newId, out string errorMessage)` | Inserts a new record into the `GroupHardware` table using `sp_GroupHardwareInsert`. Returns `ERROR_SUCCESS` (0) on success; otherwise returns a non-zero error code. `newId` is the ID of the inserted record. `errorMessage` contains the error text on failure. |
| `GroupHardwareGet` | `ulong GroupHardwareGet(IUserDbRecord user, IConnectionDetails connection, int? groupId, string serialNumber, bool? embedded, out GroupHardwareDbRecord[] groupHardwareRecords)` | Retrieves records from `GroupHardware` matching the provided filters (`groupId`, `serialNumber`, `embedded`). Returns `ERROR_SUCCESS` on success. `groupHardwareRecords` contains the result array (possibly empty). |
| `GroupHardwareDelete` | `ulong GroupHardwareDelete(IUserDbRecord user, IConnectionDetails connection, int? groupId, int? dasId, out string errorString)` | Deletes a record from `GroupHardware` identified by `groupId` and `dasId` using `sp_GroupHardwareDelete`. Returns `ERROR_SUCCESS` (0) on success; otherwise returns the stored procedures error code. `errorString` contains the error message from the stored procedure or exception. |
### `IGroups` Interface (Namespace: `DbAPI.Groups`)
| Method | Signature | Description |
|--------|-----------|-------------|
| `GroupsInsert` | `ulong GroupsInsert(IUserDbRecord user, IConnectionDetails connection, ref IGroupDbRecord group)` | Inserts a new group into the `Groups` table using `sp_GroupsInsert`. On success, `group.Id` is populated with the new records ID. Returns `ERROR_SUCCESS` (0) on success; otherwise `ERROR_UNKNOWN`. |
| `GroupsUpdate` | `ulong GroupsUpdate(IUserDbRecord user, IConnectionDetails connection, IGroupDbRecord group)` | Updates an existing group in the `Groups` table using `sp_GroupsUpdate`. Returns `ERROR_SUCCESS` (0) on success; otherwise `ERROR_UNKNOWN`. |
| `GroupsGet` | `ulong GroupsGet(IUserDbRecord user, IConnectionDetails connection, int? id, string serialNumber, string displayName, bool? embedded, int? staticGroupId, out IGroupDbRecord[] groups)` | Retrieves group records matching the provided filters (`id`, `serialNumber`, `displayName`, `embedded`, `staticGroupId`). Returns `ERROR_SUCCESS` on success. `groups` contains the result array (possibly empty). |
| `GroupsDelete` | `ulong GroupsDelete(IUserDbRecord user, IConnectionDetails connection, long id, out string errorString)` | Deletes a group identified by `id` using `sp_GroupsDelete`. Returns `ERROR_SUCCESS` (0) on success; otherwise returns the stored procedures error code. `errorString` contains the error message from the stored procedure or exception. |
> **Note**: All methods require a logged-in user (`IsUserLoggedIn` check). Failure to authenticate returns `ERROR_ACCESS_DENIED`.
---
## 3. Invariants
- **Authentication**: All public methods first verify the user is logged in via `DbAPI.Connections.IsUserLoggedIn(user, connection)`. If not, they return `ERROR_ACCESS_DENIED` without invoking the database.
- **Stored Procedure Usage**: All database interactions occur exclusively through stored procedures:
- `sp_GroupHardwareInsert`, `sp_GroupHardwareGet`, `sp_GroupHardwareDelete`
- `sp_GroupsInsert`, `sp_GroupsUpdate`, `sp_GroupsGet`, `sp_GroupsDelete`
- **Error Handling**:
- Non-zero `@errorNumber` output parameter from stored procedures indicates failure.
- On failure, the error code is returned as a `ulong`, and the `@errorMessage` output parameter is logged and/or propagated.
- Exceptions during execution result in `ERROR_UNKNOWN` (value not specified in source, but implied by usage).
- **Resource Disposal**: All `SqlCommand` objects and their connections are disposed in `finally` blocks.
- **Null Handling**:
- `GroupsInsert` treats `ExtraProperties` as empty string if `null`.
- `GroupsUpdate` passes `ExtraProperties` directly (including `null`), which may be handled by the stored procedure.
- `GroupsGet` and `GroupHardwareGet` accept nullable parameters and pass `null` to the stored procedure if not provided.
---
## 4. Dependencies
### Module Dependencies (Imports/Usings):
- **Database Abstraction**:
- `DbAPI.Connections` (`IUserDbRecord`, `IConnectionDetails`, `IsUserLoggedIn`, `ConnectionManager`)
- `DTS.Common.Interface.Database` (`IUserDbRecord`, `IConnectionDetails`)
- `System.Data`, `System.Data.SqlClient` (ADO.NET types: `SqlCommand`, `SqlParameter`, `SqlDbType`, `CommandType`, `SqlDataReader`)
- **Group/GroupHardware Models**:
- `DTS.Common.Interface.Groups` (`IGroupDbRecord`)
- `DTS.Common.Interface.Groups.GroupList` (likely contains `IGroupDbRecord` and `GroupHardwareDbRecord`)
- `DTS.Common.Classes.Groups` (`GroupHardwareDbRecord`, `GroupDbRecord`)
- **Error & Logging**:
- `DbAPI.Errors` (`ErrorCodes`)
- `DbAPI.Logging` (`LogManager`, `TraceEventType`, `LogManager.LogEvents`)
### Inferred Usage:
- **Consumers**: Likely used by higher-level business logic (e.g., test setup management, group configuration UI/backend services) that need to manage hardware-group associations and group metadata.
- **Database Dependencies**:
- Requires the following stored procedures to exist in the database:
- `sp_GroupHardwareInsert`
- `sp_GroupHardwareGet`
- `sp_GroupHardwareDelete`
- `sp_GroupsInsert`
- `sp_GroupsUpdate`
- `sp_GroupsGet`
- `sp_GroupsDelete`
---
## 5. Gotchas
- **Parameter Mismatch in `GroupHardwareDelete`**: The XML doc comment states `groupId` and `dasId` are "Id in the GroupHardware table", but the method signature uses `int? groupId` and `int? dasId`. However, the stored procedure `sp_GroupHardwareDelete` likely expects these as *composite key values* (group ID + DAS ID), not a single primary key. This may be confusing if the table uses a surrogate key (e.g., `GroupHardwareId`).
- **Inconsistent Error Code Propagation in `GroupHardwareDelete`**: On stored procedure error, `GroupHardwareDelete` throws an exception (with `errorMessage`) but then catches it and returns `ERROR_UNKNOWN`, losing the original error code. The `errorString` is only partially populated (exception message prepended), and the original error code is discarded.
- **`GroupsInsert` Uses `ref` Parameter**: The `group` parameter is `ref`, meaning the method modifies the callers object (specifically `group.Id`). This is non-idiomatic in modern C# and could surprise developers unfamiliar with this convention.
- **`GroupsUpdate` Does Not Update `Id`**: The `Id` field is used only as input (for the `WHERE` clause), and the method does not modify the `group` object on success. This is expected, but worth noting.
- **`GroupsGet` Logs `"GroupsGet failed"` in `GroupHardware` Class**: In `GroupHardware.cs`, the `GroupHardwareGet` method logs `"GroupsGet failed"` on exception — this appears to be a copy-paste error in the log message.
- **`ExtraProperties` Handling Inconsistency**: `GroupsInsert` defaults `ExtraProperties` to `""` if `null`, but `GroupsUpdate` passes `null` directly. If the stored procedure or database expects non-null, this could cause issues.
- **No Validation on Input Parameters**: No explicit validation (e.g., null checks on `serialNumber`, `displayName`) is performed before calling stored procedures — behavior depends on stored procedure robustness.
- **`GroupsDelete` Uses `long id` but Stored Procedure Uses `@Id` as `Int`**: The interface method signature uses `long id`, but the stored procedure parameter is `SqlDbType.Int`. This may cause truncation or runtime errors if `id` exceeds `Int32.MaxValue`.

View File

@@ -0,0 +1,73 @@
---
source_files:
- DataPRO/DbAPI/Logging/LogManager.cs
generated_at: "2026-04-16T04:26:52.712375+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "93d08dfeda60c1f4"
---
# Logging
### **Purpose**
This module provides centralized logging infrastructure for the `DbAPI` layer, abstracting the underlying logging mechanism (`TextLogger`) and exposing a simplified, type-safe interface for recording events with specific categories and severity levels. It ensures thread-safe initialization, enforces log level filtering, and writes all log entries to a rolling log file (`DB.log`) with standardized timestamping and formatting.
---
### **Public Interface**
All members are `internal` (not `public`), per the `internal class LogManager` declaration. However, the module exposes the following static members accessible within the `DbAPI.Logging` namespace:
- **`public static Sink DBAPILogWriter`**
A delegate of type `Sink` (from `DTS.Common.Utilities.Logging`) assigned during initialization to the `LogMsg` method. It serves as the external-facing write endpoint for log messages.
- **`public static void Initialize(int logSize, string path, int minLog)`**
Initializes the logging system exactly once per application lifetime (idempotent due to early return if `_LogWriter` is non-null). Creates a `TextLogger` writing to `{path}/DB.log`, configures log rollover, sets the minimum log level (`_MinLog`), and assigns `DBAPILogWriter`.
Parameters:
- `logSize`: Maximum size (in bytes?) for the log file before rollover.
- `path`: Directory path where `DB.log` will be written.
- `minLog`: Bitwise combination of `TraceEventType` values (cast to `int`) defining the minimum severity to log (e.g., `Error | Warning`).
- **`public static void Log(TraceEventType eventType, LogEvents logEvent, string msg)`**
Logs a message if the `eventType` meets or exceeds the configured `_MinLog` level (via bitwise AND check). Formats the message with a timestamp, event type, `LogEvents` category, and the provided `msg`, then writes it via `LogMsg`.
Parameters:
- `eventType`: Severity level (e.g., `TraceEventType.Error`, `Information`).
- `logEvent`: Category from the `LogEvents` enum.
- `msg`: The log message text.
- **`public static void OnWriteException(Exception ex)`**
Callback invoked by `TextLogger` on write failures. Writes the exception message to `Console.Error`.
---
### **Invariants**
- `_LogWriter` is initialized exactly once per AppDomain lifetime (enforced by `lock` + null-check in `Initialize`).
- Log messages are written only if `(minLog & (int)eventType) == (int)eventType` holds (i.e., the event type is *included* in the configured minimum log level).
- All log entries are prefixed with a fixed-format timestamp: `yyyy-MM-dd HH:mm:ss.fff`.
- The log file path is always `{path}/DB.log`.
- `_MinLog` is set during initialization and never modified afterward.
- `DBAPILogWriter` is assigned *only* during the first call to `Initialize` and remains non-null thereafter.
---
### **Dependencies**
- **Imports/Usings**:
- `DTS.Common.Utilities.Logging` (provides `Sink`, `TextLogger`, `APILogger`).
- `System`, `System.Diagnostics`, `System.IO`.
- **External Types Used**:
- `DTS.Common.Utilities.Logging.TextLogger` (core logging implementation).
- `DTS.Common.Utilities.Logging.APILogger` (only referenced for `Sink` type via `using static`).
- `System.TraceEventType` (severity levels).
- **Depended Upon By**:
- Other classes in the `DbAPI` layer (inferred from namespace `DbAPI.Logging` and `DBAPILogWriter` usage).
- *Not* directly consumed by external consumers (all members are `internal`).
---
### **Gotchas**
- **Thread Safety**: `Initialize` uses a `lock` to prevent re-initialization, but `Log` and `LogMsg` are *not* explicitly synchronized. However, `TextLogger` is assumed to be thread-safe (typical for such utilities).
- **Silent Failures**: `LogMsg` catches and discards all exceptions during logging, potentially masking I/O errors (e.g., disk full, permission issues).
- **Hardcoded Log Filename**: The log file is always named `DB.log`, with no configurability.
- **Log Level Semantics**: `_MinLog` uses a bitwise mask against `TraceEventType` values. The default (`Error | Warning`) means `Information`, `Verbose`, etc., are excluded unless explicitly enabled.
- **No Cleanup**: No `Dispose` or shutdown logic is provided; resources (e.g., file handles) persist until process termination.
- **Timestamp Format**: Milliseconds are zero-padded to 3 digits, but no timezone info is included (local time only).
- **Enum Naming**: `LogEvents` is poorly named (implies *events* like "Connections", but actually represents *categories* or *topics*).

View File

@@ -0,0 +1,41 @@
---
source_files:
- DataPRO/DbAPI/SPCaching/SPCache.cs
generated_at: "2026-04-16T04:26:17.028428+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "d93a7e3ae4a08e22"
---
# SPCaching
## 1. Purpose
The `SPCache` class serves as a lightweight data container for caching the mapping between client and database versions to a specific stored procedure version. Its role is to avoid redundant version-resolution logic—once the appropriate stored procedure version has been determined for a given `(ClientVersion, DbVersion)` pair, it can be reused by storing it in an `SPCache` instance. This is intended for scenarios where version compatibility decisions are expensive or frequently repeated, and where the mapping is assumed stable after initial resolution.
## 2. Public Interface
The class exposes only public auto-implemented properties; there are no methods or constructors defined.
- **`int ClientVersion { get; set; }`**
Stores the application or client version used as input to the version-resolution logic.
- **`int DbVersion { get; set; }`**
Stores the database version used as input to the version-resolution logic.
- **`int StoredProcedureVersion { get; set; }`**
Stores the resolved stored procedure version corresponding to the `ClientVersion` and `DbVersion`.
## 3. Invariants
- The class itself enforces no invariants beyond the type of its properties (all are non-nullable `int` properties with default values of `0` if unset).
- There is no validation or consistency check on the relationship between `ClientVersion`, `DbVersion`, and `StoredProcedureVersion`. For example, no rule enforces that `StoredProcedureVersion` must be ≤ `ClientVersion` or match any particular schema.
- The documentation implies that once a mapping is determined, it is assumed stable ("once this has been determined we don't need to determine it again ... usually"), but this is a *usage convention*, not a runtime invariant.
## 4. Dependencies
- **No external dependencies**: The file contains no `using` directives, indicating no runtime dependencies on other namespaces or libraries.
- **Depended on by**: Not visible in this source file. Based on the namespace (`DbAPI.SPCaching`) and class purpose, it is likely consumed by components responsible for database interaction or stored procedure dispatch (e.g., a data access layer), but such consumers are not specified here.
## 5. Gotchas
- **No equality or hashing support**: The class does not override `Equals`, `GetHashCode`, or implement `IEquatable<SPCache>`. Two instances with identical property values will not be considered equal by reference-based comparison unless explicitly handled by consumers.
- **Mutable by design**: All properties are `public set`, so the cache entry can be modified after creation. This may lead to stale or inconsistent entries if reused carelessly.
- **No version conflict detection**: If the same `(ClientVersion, DbVersion)` pair maps to multiple `StoredProcedureVersion` values over time (e.g., due to schema changes), the cache provides no mechanism to invalidate or detect such mismatches.
- **Ambiguous version semantics**: The documentation does not clarify how `StoredProcedureVersion` is derived (e.g., is it the minimum, maximum, or exact match version?), nor whether negative or zero values are valid. Consumers must infer this from external logic.
- **Thread-safety not addressed**: The class has no synchronization primitives; concurrent access to a shared instance is unsafe unless managed externally.

View File

@@ -0,0 +1,135 @@
---
source_files:
- DataPRO/DbAPI/Sensors/ISensors.cs
generated_at: "2026-04-16T04:25:20.635650+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "c1fec90650918e9b"
---
# Documentation: `ISensors` Interface (`DbAPI.Sensors.ISensors`)
---
## 1. Purpose
The `ISensors` interface defines the contract for sensor-related data access operations in the `DbAPI.Sensors` module. It provides a unified set of methods to query, insert, update, and delete various sensor types—including analog sensors, digital inputs/outputs, squibs, UARTs, CAN, thermocouples, and input/output streams—as well as associated diagnostic runs, calibrations, and usage metadata. This interface abstracts database interactions for sensor data, enabling consistent CRUD operations across the system while enforcing user context (`IUserDbRecord`) and connection context (`IConnectionDetails`) for security and multi-tenancy.
---
## 2. Public Interface
All methods return `ulong`, where `0` (`ERROR_SUCCESS`) indicates success and non-zero values indicate error codes.
### Query Methods
| Method | Signature | Description |
|--------|-----------|-------------|
| `SensorsAnalogDiagnosticsGet` | `ulong SensorsAnalogDiagnosticsGet(IUserDbRecord user, IConnectionDetails connection, long? Id, long? diagnosticRunId, int? sensorId, string sensorSerialNumber, out IDiagnosticEntry[] records)` | Retrieves analog diagnostic records matching optional filters (`Id`, `diagnosticRunId`, `sensorId`, `sensorSerialNumber`). Returns all matching `IDiagnosticEntry` records. |
| `SensorsAnalogDiagnosticRunGet` | `ulong SensorsAnalogDiagnosticRunGet(IUserDbRecord user, IConnectionDetails connection, long? Id, int? testId, string testName, out IDiagnosticRun[] records)` | Retrieves diagnostic run records matching optional filters (`Id`, `testId`, `testName`). |
| `SensorsInputStreamGet` | `ulong SensorsInputStreamGet(IUserDbRecord user, IConnectionDetails connection, int? Id, string SerialNumber, out IStreamInputRecord[] records)` | Retrieves input stream records matching `Id` or `SerialNumber`. |
| `SensorsOutputStreamGet` | `ulong SensorsOutputStreamGet(IUserDbRecord user, IConnectionDetails connection, int? Id, string SerialNumber, out IStreamOutputRecord[] records)` | Retrieves output stream records matching `Id` or `SerialNumber`. |
| `SensorsThermocouplerGet` | `ulong SensorsThermocouplerGet(IUserDbRecord user, IConnectionDetails connection, int clientDbVersion, int? Id, string SerialNumber, out IThermocouplerRecord[] records)` | Retrieves thermocouple records matching `Id` or `SerialNumber`. Includes `clientDbVersion` parameter—likely for schema/version compatibility. |
| `SensorsUARTGet` | `ulong SensorsUARTGet(IUserDbRecord user, IConnectionDetails connection, int? Id, string SerialNumber, out IUARTRecord[] records)` | Retrieves UART configuration records matching `Id` or `SerialNumber`. |
| `SensorsCanGet` | `ulong SensorsCanGet(IUserDbRecord user, IConnectionDetails connection, int? Id, string SerialNumber, out ICANRecord[] records)` | Retrieves CAN configuration records matching `Id` or `SerialNumber`. |
| `SensorsDigitalOutGet` | `ulong SensorsDigitalOutGet(IUserDbRecord user, IConnectionDetails connection, int? Id, string serialNumber, out IDigitalOutDbRecord[] records)` | Retrieves digital output settings matching `Id` or `serialNumber`. |
| `SensorsDigitalInGet` | `ulong SensorsDigitalInGet(IUserDbRecord user, IConnectionDetails connection, int? id, string serialNumber, string eId, out IDigitalInDbRecord[] records)` | Retrieves digital input settings matching `id`, `serialNumber`, or `eId` (electronic ID). |
| `SensorsSquibGet` | `ulong SensorsSquibGet(IUserDbRecord user, IConnectionDetails connection, int? Id, string serialNumber, string eId, out ISquibDbRecord[] records)` | Retrieves squib settings matching `Id`, `serialNumber`, or `eId`. |
| `SensorsAnalogBridgeResistanceGet` | `ulong SensorsAnalogBridgeResistanceGet(IUserDbRecord user, IConnectionDetails connection, string serialNumber, out double bridgeResistance)` | Retrieves the bridge resistance (in ohms) for the analog sensor identified by `serialNumber`. |
| `SensorCalibrationsGet` | `ulong SensorCalibrationsGet(IUserDbRecord user, IConnectionDetails connection, int? sensorId, string serialNumber, out ISensorCalDbRecord[] calibrations)` | Retrieves calibration records matching `sensorId` or `serialNumber`. |
| `SensorsAnalogGet` | `ulong SensorsAnalogGet(IUserDbRecord user, IConnectionDetails connection, int? sensorId, string serialNumber, string eId, out IAnalogDbRecord[] sensors)` | Retrieves analog sensor records matching `sensorId`, `serialNumber`, or `eId` (DALLAS/TEDS ID). |
| `SensorsGet` | `ulong SensorsGet(IUserDbRecord user, IConnectionDetails connection, string serialNumber, out ISensorDbRecord[] sensors)` | Retrieves generic sensor records (base type `ISensorDbRecord`) matching `serialNumber`. |
### Insert/Update Methods
| Method | Signature | Description |
|--------|-----------|-------------|
| `SensorsAnalogDiagnosticUpdateInsert` | `ulong SensorsAnalogDiagnosticUpdateInsert(IUserDbRecord user, IConnectionDetails connection, ref IDiagnosticEntry entry)` | Inserts or updates a single `IDiagnosticEntry`. **Note**: Parameter is `ref`, implying the record may be mutated (e.g., to populate `Id` on insert). |
| `SensorsAnalogDiagnosticRunUpdateInsert` | `ulong SensorsAnalogDiagnosticRunUpdateInsert(IUserDbRecord user, IConnectionDetails connection, ref IDiagnosticRun run)` | Inserts or updates a diagnostic run (`IDiagnosticRun`). `ref` parameter allows mutation of `Id` on insert. |
| `SensorsInputStreamUpdateInsert` | `ulong SensorsInputStreamUpdateInsert(IUserDbRecord user, IConnectionDetails connection, ref IStreamInputRecord record)` | Inserts or updates an input stream record. `ref` parameter allows mutation of `Id` on insert. |
| `SensorsOutputStreamUpdateInsert` | `ulong SensorsOutputStreamUpdateInsert(IUserDbRecord user, IConnectionDetails connection, ref IStreamOutputRecord record)` | Inserts or updates an output stream record. `ref` parameter allows mutation of `Id` on insert. |
| `SensorsUARTUpdateInsert` | `ulong SensorsUARTUpdateInsert(IUserDbRecord user, IConnectionDetails connection, ref IUARTRecord record)` | Inserts or updates a UART record. `ref` parameter allows mutation of `Id` on insert. |
| `SensorsCanUpdateInsert` | `ulong SensorsCanUpdateInsert(IUserDbRecord user, IConnectionDetails connection, ref ICANRecord record)` | Inserts or updates a CAN record. `ref` parameter allows mutation of `Id` on insert. |
| `SensorsDigitalOutUpdateInsert` | `ulong SensorsDigitalOutUpdateInsert(IUserDbRecord user, IConnectionDetails connection, IDigitalOutDbRecord record)` | Inserts or updates a digital output record. **Note**: Parameter is *not* `ref`; record is passed by value. Mutation of `Id` on insert is *not* possible unless `IDigitalOutDbRecord` is a mutable reference type. |
| `SensorsDigitalInUpdateInsert` | `ulong SensorsDigitalInUpdateInsert(IUserDbRecord user, IConnectionDetails connection, IDigitalInDbRecord record)` | Inserts or updates a digital input record. **Not `ref`**—same caveats as `SensorsDigitalOutUpdateInsert`. |
| `SensorsSquibUpdateInsert` | `ulong SensorsSquibUpdateInsert(IUserDbRecord user, IConnectionDetails connection, ISquibDbRecord record)` | Inserts or updates a squib record. **Not `ref`**—same caveats. |
| `SensorsAnalogUpdateInsert` | `ulong SensorsAnalogUpdateInsert(IUserDbRecord user, IConnectionDetails connection, IAnalogDbRecord record)` | Commits an analog sensor record. **Not `ref`**—same caveats. |
| `SensorCalibrationsInsert` | `ulong SensorCalibrationsInsert(IUserDbRecord user, IConnectionDetails connection, ISensorCalDbRecord cal, int sensorType, bool setCalibrationId)` | Inserts a calibration record. `setCalibrationId` likely controls whether the inserted records `Id` is populated. |
### Delete Methods
| Method | Signature | Description |
|--------|-----------|-------------|
| `SensorsDeleteAll` | `ulong SensorsDeleteAll(IUserDbRecord user, IConnectionDetails connection)` | Deletes **all** sensor records in the database. |
| `SensorCalibrationsDelete` | `ulong SensorCalibrationsDelete(IUserDbRecord user, IConnectionDetails connection, string sensorSerialNumber, DateTime? calibrationDate, DateTime? modifyDate)` | Deletes calibration records matching optional filters. `null` parameters act as wildcards. |
| `SensorsDelete` | `ulong SensorsDelete(IUserDbRecord user, IConnectionDetails connection, int sensorId, int sensorType)` | Deletes sensors matching `sensorId` and `sensorType`. `sensorType` values: `0` = analog, `1` = digital in, `2` = digital out, `3` = squib, `4` = UART. |
### Usage Count Update Methods
| Method | Signature | Description |
|--------|-----------|-------------|
| `UpdateAssemblySensorUsageCount` | `ulong UpdateAssemblySensorUsageCount(IUserDbRecord user, IConnectionDetails connection, string assemblyName, int newUsageCount)` | Updates the usage count for a named assembly. |
| `UpdateSensorUsageCount` | `ulong UpdateSensorUsageCount(IUserDbRecord user, IConnectionDetails connection, ISensorData sd)` | Updates the total usage count for a sensor (type `ISensorData`). |
| `UpdateSensorCalibrationUsageCount` | `ulong UpdateSensorCalibrationUsageCount(IUserDbRecord user, IConnectionDetails connection, int sensorId, int sensorCalibrationId, int newUsageCount)` | Updates the usage count for a specific calibration of a sensor. |
---
## 3. Invariants
- **User & Connection Context**: All methods require `IUserDbRecord user` and `IConnectionDetails connection`. These are mandatory and must be non-null (enforced by caller).
- **Return Value Semantics**: All methods return `ulong`; `0` means success. Non-zero values are error codes (specific codes not defined in this interface).
- **Wildcard Filtering**: For all `Get` methods, `null` parameters act as wildcards (match all). For `SensorsUARTGet`, `null` or empty `SerialNumber` matches all.
- **Insert Behavior**: For methods with `ref` parameters (e.g., `SensorsAnalogDiagnosticUpdateInsert`, `SensorsInputStreamUpdateInsert`), the record passed in may be mutated to include the newly assigned database `Id` upon successful insert.
- **Sensor Type Constants**: `SensorsDelete` uses integer sensor types defined as:
`0` = analog, `1` = digital in, `2` = digital out, `3` = squib, `4` = UART.
*(Note: These are documented in the XML comment for `SensorsDelete`, but no constants are defined in this interface.)*
- **Calibration Insert Specifics**: `SensorCalibrationsInsert` requires `sensorType` and `setCalibrationId`—the latter likely controls whether the inserted records `Id` is populated.
---
## 4. Dependencies
### Imports / Dependencies of `ISensors`:
- `DbAPI.Connections``IConnectionDetails`
- `DTS.Common.Interface.Database``IUserDbRecord`
- `DTS.Common.Interface.Sensors``ISensorDbRecord`, `ISensorData`, `ISensorCalDbRecord`, `IAnalogDbRecord`, `IDigitalInDbRecord`, `IDigitalOutDbRecord`, `ISquibDbRecord`, `IUARTRecord`, `ICANRecord`
- `DTS.Common.Interface.Sensors.AnalogDiagnostics``IDiagnosticEntry`, `IDiagnosticRun`
- `System``DateTime`, `ulong`
### Implemented By:
- Not visible in this file. Presumably implemented by a concrete class (e.g., `Sensors` in the same namespace or a derived layer like `DbAPI.Sensors.Sensors`).
### Used By:
- Any module requiring sensor data access, likely including:
- UI layers (e.g., WPF/WinForms)
- Business logic layers (e.g., service classes)
- Import/export utilities
- Diagnostic or test execution modules
---
## 5. Gotchas
- **Inconsistent `ref` usage**:
- Methods like `SensorsAnalogDiagnosticUpdateInsert`, `SensorsInputStreamUpdateInsert`, etc., use `ref` for the record parameter, implying the record may be mutated (e.g., to populate `Id` on insert).
- Others (e.g., `SensorsDigitalOutUpdateInsert`, `SensorsAnalogUpdateInsert`, `SensorCalibrationsInsert`) pass records *by value* (no `ref`). If `IDigitalOutDbRecord`, `IAnalogDbRecord`, etc., are *mutable reference types*, the implementation may still mutate them—but this is not guaranteed by the interface and may lead to confusion or bugs if callers expect `Id` to be updated.
- **`clientDbVersion` in `SensorsThermocouplerGet`**:
The `int clientDbVersion` parameter is present but not documented beyond its presence. Its purpose (e.g., schema versioning, backward compatibility) is unclear from this interface alone.
- **`SensorsGet` vs `SensorsAnalogGet`**:
`SensorsGet` returns `ISensorDbRecord[]`, while `SensorsAnalogGet` returns `IAnalogDbRecord[]`. The relationship between these types is not defined here—`ISensorDbRecord` may be a base interface, but callers must be cautious about casting.
- **`SensorsDeleteAll` is dangerous**:
No filtering is possible—this deletes *all* sensors. Use with extreme caution.
- **`SensorsAnalogBridgeResistanceGet` returns `double`**:
No unit is specified in the interface, though the XML comment says “bridge resistance of sensor.” Assumed to be ohms, but not guaranteed.
- **`sensorType` is magic-numbered**:
Sensor types are documented inline (`0` = analog, etc.), but no enum or constants are defined in this interface. Callers must hardcode or define their own constants.
- **`setCalibrationId` in `SensorCalibrationsInsert`**:
Purpose is unclear from signature alone—likely controls whether the inserted records `Id` is set, but behavior is not documented.
- **No async methods**:
All methods are synchronous. In modern codebases, this may indicate legacy design or require wrapping for async compatibility.
> **None identified from source alone** for other potential gotchas (e.g., concurrency, transaction semantics), as those are not exposed in this interface.
---
*End of Documentation*

View File

@@ -0,0 +1,119 @@
---
source_files:
- DataPRO/DbAPI/Tags/ITags.cs
- DataPRO/DbAPI/Tags/Tags.cs
generated_at: "2026-04-16T04:26:07.847567+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "70ba67f8176108c5"
---
# Documentation: `DbAPI.Tags` Module
## 1. Purpose
This module provides a data access layer for managing *tags* and *tag assignments* in the database. Tags are user-defined labels (text strings with metadata like `IsObsolete`) that can be associated with various object types (e.g., assets, locations, events) via assignment records. The `ITags` interface and its concrete implementation `Tags` expose CRUD operations for both tags and their assignments, enforcing user authentication and delegating to stored procedures (`sp_*`) for database interaction. It serves as the canonical interface for tag-related operations across the system, abstracting SQL Server-specific logic.
## 2. Public Interface
### Interface: `ITags`
All methods return `ulong`, where `0` (`ERROR_SUCCESS`) indicates success and non-zero values indicate error codes (e.g., from `ErrorCodes`).
#### `TagAssignmentsInsert`
```csharp
ulong TagAssignmentsInsert(IUserDbRecord user, IConnectionDetails connection, ITagAssignment tagAssignment);
```
- Inserts a new tag assignment record (linking a tag to an object of a given type).
- Validates user login and `tagAssignment` non-null.
- Uses stored procedure `sp_TagAssignmentsInsert`.
#### `TagAssignmentsDelete`
```csharp
ulong TagAssignmentsDelete(IUserDbRecord user, IConnectionDetails connection, int objectId, TagTypes tagType);
```
- Deletes *all* tag assignments for a given `objectId` and `tagType`.
- Validates user login.
- Uses stored procedure `sp_TagAssignmentsDelete`.
#### `TagAssignmentsGet`
```csharp
ulong TagAssignmentsGet(IUserDbRecord user, IConnectionDetails connection, TagTypes? tagType, out ITagAssignment[] tagAssignments);
```
- Retrieves all tag assignments, optionally filtered by `tagType`. Pass `null` for `tagType` to get *all* assignments.
- Returns assignments in the `tagAssignments` out parameter (never `null`; empty array if none found).
- Uses stored procedure `sp_TagAssignmentsGet`.
#### `TagsInsert`
```csharp
ulong TagsInsert(IUserDbRecord user, IConnectionDetails connection, ref ITag tag);
```
- Inserts a new tag. **Modifies the `tag` parameter in-place** by setting its `ID` field to the newly generated database ID.
- Validates user login and `tag` non-null.
- Uses stored procedure `sp_TagsInsert`.
- Fails if no new ID is returned from the stored procedure.
#### `TagsGetId`
```csharp
ulong TagsGetId(IUserDbRecord user, IConnectionDetails connection, string tagText, out int? id);
```
- Looks up the database ID for a given `tagText`.
- Returns `id = null` if no matching tag exists.
- Uses stored procedure `sp_TagsGetId`.
#### `TagsDelete`
```csharp
ulong TagsDelete(IUserDbRecord user, IConnectionDetails connection, int tagId);
```
- Deletes the tag with the specified `tagId`. **Note**: Does *not* automatically delete associated assignments (handled by DB constraints/triggers or separate logic).
- Validates user login.
- Uses stored procedure `sp_TagsDelete`.
#### `TagsGet`
```csharp
ulong TagsGet(IUserDbRecord user, IConnectionDetails connection, int? tagId, out ITag[] records);
```
- Retrieves tags matching `tagId`. Pass `null` for `tagId` to get *all* tags.
- Returns results in `records` out parameter (never `null`; empty array if none found).
- Uses stored procedure `sp_TagsGet`.
---
### Class: `Tags`
Concrete implementation of `ITags`. All methods follow the same signatures and behaviors as described above.
## 3. Invariants
- **Authentication**: All methods require the user to be logged in. If `IsUserLoggedIn(user, connection)` returns `false`, the method returns `ErrorCodes.ERROR_ACCESS_DENIED` *before* executing any database logic.
- **Parameter Validation**: `TagAssignmentsInsert` and `TagsInsert` require non-null `tagAssignment`/`tag` respectively; otherwise, return `ErrorCodes.ERROR_MISSING_PARAMETER`.
- **Output Parameters**: `TagAssignmentsGet` and `TagsGet` initialize their `out` arrays to `new T[0]` *before* any logic, ensuring they are never `null` on return.
- **ID Assignment**: `TagsInsert` *mutates* the input `tag` object by assigning its `ID` property to the output ID from the stored procedure.
- **Error Handling**: All database errors (non-zero `@errorNumber` from stored procedures) or exceptions during execution result in `ErrorCodes.ERROR_UNKNOWN` being returned. Errors are logged via `LogManager`.
- **Resource Cleanup**: All database connections and commands are disposed in `finally` blocks.
## 4. Dependencies
### This module depends on:
- `DbAPI.Connections` (`IUserDbRecord`, `IConnectionDetails`, `IsUserLoggedIn`, `ConnectionManager`)
- `DbAPI.Errors` (`ErrorCodes` constants)
- `DbAPI.Logging` (`LogManager`, `TraceEventType`, `LogManager.LogEvents.Tags`)
- `DTS.Common.Interface.Tags` (`ITag`, `ITagAssignment`, `TagTypes`)
- `DTS.Common.Classes.Tags` (`TagAssignment`, `Tag` — concrete implementations of `ITagAssignment` and `ITag`)
- `System.Data`, `System.Data.SqlClient` — for ADO.NET interaction (SQL Server)
### This module is depended on by:
- Any code requiring tag/tag-assignment operations (e.g., UI layers, business logic modules). Not explicitly stated in source, but implied by its role as a core data interface.
## 5. Gotchas
- **`TagsInsert` mutates its input**: The `tag` parameter is passed `ref`, and its `ID` is overwritten with the new database ID. Callers must be aware this modifies the passed object.
- **`TagsGetId` accepts `null` `tagText`**: The method handles `tagText == null` gracefully (returns `id = null`), but callers should be cautious about passing `null` unintentionally.
- **`TagAssignmentsDelete` deletes *all* assignments for an object**: Passing `objectId` and `tagType` will delete *all* tag assignments for that object, not just one. Ensure this is the intended behavior.
- **No automatic assignment cleanup on `TagsDelete`**: Deleting a tag via `TagsDelete` does *not* remove existing assignments referencing it. The database likely enforces referential integrity (e.g., `ON DELETE CASCADE`), but this is not handled in this module and should be verified at the DB level.
- **`TagTypes?` vs `TagTypes`**: `TagAssignmentsGet` accepts `TagTypes?` (nullable), while `TagAssignmentsDelete` and `TagsInsert` use non-nullable `TagTypes`. Ensure callers pass valid enum values (not `null`) for the latter.
- **Stored Procedure Assumptions**: Behavior is tightly coupled to the underlying stored procedures (`sp_*`). Changes to their signatures or logic may break this module silently.
- **No update method**: There is no `TagsUpdate` or `TagAssignmentsUpdate` method — updates are likely handled via `TagsInsert` (if ID already exists, stored procedure may upsert) or external logic. This is not documented in the interface, so callers should verify behavior.
- **String length not validated**: `TagsInsert` passes `tag.Text` directly to `@TagText (NVARCHAR(255))`. If `tag.Text` exceeds 255 characters, the stored procedure or DB will fail — no client-side validation is performed.
None identified beyond the above.

View File

@@ -0,0 +1,120 @@
---
source_files:
- DataPRO/DbAPI/TestMetaData/ICustomerDetails.cs
- DataPRO/DbAPI/TestMetaData/ILabratoryDetails.cs
- DataPRO/DbAPI/TestMetaData/ITestEngineerDetails.cs
- DataPRO/DbAPI/TestMetaData/CustomerDetails.cs
- DataPRO/DbAPI/TestMetaData/TestEngineerDetails.cs
- DataPRO/DbAPI/TestMetaData/LabratoryDetails.cs
generated_at: "2026-04-16T04:25:29.306320+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "440dcd3555714b4f"
---
# Test Metadata Database API Module Documentation
## 1. Purpose
This module provides a standardized database abstraction layer for CRUD operations on three core metadata entity types—Customer, Laboratory, and Test Engineer details—within the system's database. It encapsulates direct SQL Server interactions via stored procedures (`sp_*`) and enforces user authentication, error handling, and connection management. The module exists to decouple business logic from low-level data access, ensuring consistent error reporting (via `ulong` return codes), centralized logging, and reuse of common database patterns across the application.
## 2. Public Interface
All interfaces reside in the `DbAPI.TestMetaData` namespace (e.g., `ICustomerDetails`, `ILabratoryDetails`, `ITestEngineerDetails`). Implementation classes (`CustomerDetails`, `LabratoryDetails`, `TestEngineerDetails`) are internal and implement their respective interfaces.
### `ICustomerDetails`
- **`ulong CustomerDetailsInsert(IUserDbRecord user, IConnectionDetails connection, CustomerDetailsDbRecord customerDetailsDbRecord, out int newId, out string errorString)`**
Inserts a new record into the `CustomerDetails` table via `sp_CustomerDetailsInsert`. Returns `ERROR_SUCCESS` (0) on success; `newId` holds the generated ID. `errorString` may contain SP-level error details.
- **`ulong CustomerDetailsUpdate(IUserDbRecord user, IConnectionDetails connection, CustomerDetailsDbRecord customerDetailsDbRecord, out string errorString)`**
Updates an existing `CustomerDetails` record via `sp_CustomerDetailsUpdate`. Returns `ERROR_SUCCESS` on success. `errorString` may contain SP-level error details.
- **`ulong CustomerDetailsGet(IUserDbRecord user, IConnectionDetails connection, string name, out ICustomerDetailsDbRecord[] customerDetailsDbRecords)`**
Retrieves all `CustomerDetails` records matching the `name` filter via `sp_CustomerDetailsGet`. Returns `ERROR_SUCCESS` on success; `customerDetailsDbRecords` is an array of valid (non-`IsInvalidBlank()`) records, or `null` if none found.
- **`ulong CustomerDetailsDelete(IUserDbRecord user, IConnectionDetails connection, string name, out string errorString)`**
Deletes `CustomerDetails` records matching `name` via `sp_CustomerDetailsDelete`. Returns `ERROR_SUCCESS` on success. `errorString` may contain SP-level error details.
### `ILabratoryDetails`
- **`ulong LabratoryDetailsInsert(IUserDbRecord user, IConnectionDetails connection, LabratoryDetailsDbRecord labratoryDetailsDbRecord, out int newId, out string errorString)`**
Inserts a new `LabratoryDetails` record via `sp_LabratoryDetailsInsert`. Returns `ERROR_SUCCESS` on success; `newId` holds the generated ID. `errorString` may contain SP-level error details.
- **`ulong LabratoryDetailsUpdate(IUserDbRecord user, IConnectionDetails connection, LabratoryDetailsDbRecord labratoryDetailsDbRecord, out string errorString)`**
Updates an existing `LabratoryDetails` record via `sp_LabratoryDetailsUpdate`. Returns `ERROR_SUCCESS` on success. `errorString` may contain SP-level error details.
- **`ulong LabratoryDetailsUpdateInsert(IUserDbRecord user, IConnectionDetails connection, LabratoryDetailsDbRecord labratoryDetailsDbRecord, out string errorString)`**
Performs an upsert (update or insert) on `LabratoryDetails` via `sp_LabratoryDetailsUpdateInsert`. Returns `ERROR_SUCCESS` on success. `errorString` may contain SP-level error details. *Note: No `newId` output is provided for this operation.*
- **`ulong LabratoryDetailsGet(IUserDbRecord user, IConnectionDetails connection, string name, out ILabratoryDetailsDbRecord[] labratoryDetailsDbRecords)`**
Retrieves `LabratoryDetails` records matching `name` via `sp_LabratoryDetailsGet`. Returns `ERROR_SUCCESS` on success; `labratoryDetailsDbRecords` is an array of valid records, or `null` if none found.
- **`ulong LabratoryDetailsDelete(IUserDbRecord user, IConnectionDetails connection, string name, out string errorString)`**
Deletes `LabratoryDetails` records matching `name` via `sp_LabratoryDetailsDelete`. Returns `ERROR_SUCCESS` on success. `errorString` may contain SP-level error details.
### `ITestEngineerDetails`
- **`ulong TestEngineerDetailsInsert(IUserDbRecord user, IConnectionDetails connection, TestEngineerDetailsDbRecord testEngineerDetailsDbRecord, out int newId, out string errorString)`**
Inserts a new `TestEngineerDetails` record via `sp_TestEngineerDetailsInsert`. Returns `ERROR_SUCCESS` on success; `newId` holds the generated ID. `errorString` may contain SP-level error details.
- **`ulong TestEngineerDetailsUpdate(IUserDbRecord user, IConnectionDetails connection, TestEngineerDetailsDbRecord testEngineerDetailsDbRecord, out string errorString)`**
Updates an existing `TestEngineerDetails` record via `sp_TestEngineerDetailsUpdate`. Returns `ERROR_SUCCESS` on success. `errorString` may contain SP-level error details.
- **`ulong TestEngineerDetailsUpdateInsert(IUserDbRecord user, IConnectionDetails connection, TestEngineerDetailsDbRecord testEngineerDetailsDbRecord, out string errorString)`**
Performs an upsert on `TestEngineerDetails` via `sp_TestEngineerDetailsUpdateInsert`. Returns `ERROR_SUCCESS` on success. `errorString` may contain SP-level error details. *Note: No `newId` output is provided for this operation.*
- **`ulong TestEngineerDetailsGet(IUserDbRecord user, IConnectionDetails connection, string name, out ITestEngineerDetailsDbRecord[] testEngineerDetailsDbRecords)`**
Retrieves `TestEngineerDetails` records matching `name` via `sp_TestEngineerDetailsGet`. Returns `ERROR_SUCCESS` on success; `testEngineerDetailsDbRecords` is an array of valid records, or `null` if none found.
- **`ulong TestEngineerDetailsDelete(IUserDbRecord user, IConnectionDetails connection, string name, out string errorString)`**
Deletes `TestEngineerDetails` records matching `name` via `sp_TestEngineerDetailsDelete`. Returns `ERROR_SUCCESS` on success. `errorString` may contain SP-level error details.
## 3. Invariants
- **User Authentication**: All public methods first verify user login via `DbAPI.Connections.IsUserLoggedIn(user, connection)`. If false, they return `ErrorCodes.ERROR_ACCESS_DENIED` without executing any DB operation.
- **Connection Management**: All methods use `ConnectionManager.GetSqlCommand(...)` to obtain a `SqlCommand`. The underlying connection is disposed in `finally` blocks after execution.
- **Error Reporting**:
- Return value is a `ulong` error code (0 = `ERROR_SUCCESS`).
- SP-level errors are captured via `@errorNumber` (output) and `@errorMessage` (output) parameters.
- If SP reports an error (`@errorNumber != 0`), the return value is set to `@errorNumber` (cast to `ulong`).
- Exceptions during execution are caught, logged (via `LogManager.Log`), and concatenated into the `errorString` output (format: `"{exception message}; {SP error string}"`).
- **Record Validation**: `Get` methods filter out records where `IsInvalidBlank()` returns `true`.
- **Stored Procedure Naming**: Each operation maps to a specific stored procedure:
- Insert → `sp_*Insert`
- Update → `sp_*Update`
- Upsert → `sp_*UpdateInsert`
- Get → `sp_*Get`
- Delete → `sp_*Delete`
- **Parameter Consistency**: All insert/update/upsert operations pass the same set of fields (e.g., `@Name`, `@LastModified`, `@Version = 1`) to their respective stored procedures.
## 4. Dependencies
### Module Dependencies (Imports/Usings):
- **Core Infrastructure**:
- `DbAPI.Connections` (`IUserDbRecord`, `IConnectionDetails`, `IsUserLoggedIn`, `ConnectionManager`)
- `DbAPI.Errors` (`ErrorCodes`, e.g., `ERROR_ACCESS_DENIED`, `ERROR_SUCCESS`, `ERROR_UNKNOWN`)
- `DbAPI.Logging` (`LogManager`, `TraceEventType`, `LogEvents.DataRecorders`)
- **Data Models**:
- `DTS.Common.Interface.Database` (`IUserDbRecord`, `IConnectionDetails`)
- `DTS.Common.Interface.TestMetaData` (`ICustomerDetailsDbRecord`, `ILabratoryDetailsDbRecord`, `ITestEngineerDetailsDbRecord`)
- `DTS.Common.Classes.*` (concrete record types: `CustomerDetailsDbRecord`, `LabratoryDetailsDbRecord`, `TestEngineerDetailsDbRecord`)
- **System**: `System.Data`, `System.Data.SqlClient`, `System.Diagnostics`, `System.Collections.Generic`
### Module Dependents:
- This module is consumed by higher-level components that require test metadata management (e.g., UI layers, test orchestration services).
- The interfaces (`ICustomerDetails`, etc.) are likely consumed via dependency injection or factory patterns (not visible in source, but implied by interface design).
## 5. Gotchas
- **Typo in Namespace/Class Names**: The module consistently uses `Labratory` (misspelled) instead of `Laboratory` (e.g., `ILabratoryDetails`, `LabratoryDetailsDbRecord`). This is preserved in the source and must be respected to avoid mismatches with database objects.
- **Inconsistent Parameter Usage in Delete**:
- `CustomerDetailsDelete` passes `@Name` with `null` if `name` is empty.
- `TestEngineerDetailsDelete` passes `@Name` with `DBNull.Value` if `name` is empty.
- `LabratoryDetailsDelete` passes `@LabratoryName` (not `@Name`) with `DBNull.Value` if `name` is empty.
This suggests stored procedure parameter names may differ across entities—verify SP signatures.
- **Missing `@LocalOnly` in `TestEngineerDetailsUpdate`**: The `TestEngineerDetailsUpdate` method has a commented-out line for `@LocalOnly`. This may be intentional (e.g., field not updatable) or accidental tech debt.
- **No Upsert Returns New ID**: `UpdateInsert` methods for `LabratoryDetails` and `TestEngineerDetails` do **not** output the new ID (unlike `Insert`), even though the SPs define `@new_id`. This is inconsistent and may require future correction.
- **Error String Handling**: `errorString` is only populated for `Insert`, `Update`, and `Delete` methods when SP reports an error or an exception occurs. `Get` methods do not populate `errorString`—errors are logged and `ERROR_UNKNOWN` is returned.
- **Null/Empty Name Handling**: `Get` and `Delete` methods accept `name` as a filter. `Delete` methods handle empty `name` differently per entity (see above), but `Get` passes `name` directly to the SP (including empty string), which may have unintended behavior if SP does not guard against it.
- **No Concurrency Control**: No versioning or timestamp checks are visible in the client-side code (though `@Version = 1` is passed to SPs). Concurrency handling is entirely delegated to stored procedures.

View File

@@ -0,0 +1,181 @@
---
source_files:
- DataPRO/DbAPI/TestSetups/IGraphs.cs
- DataPRO/DbAPI/TestSetups/ICalculatedChannels.cs
- DataPRO/DbAPI/TestSetups/IRegionsOfInterest.cs
- DataPRO/DbAPI/TestSetups/ITestSetups.cs
- DataPRO/DbAPI/TestSetups/CalculatedChannels.cs
- DataPRO/DbAPI/TestSetups/Graphs.cs
generated_at: "2026-04-16T04:27:03.727990+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "7a6cc3dcbca85c45"
---
# Documentation: TestSetups Module (DbAPI)
## 1. Purpose
This module provides data access layer (DAL) interfaces and implementations for managing test setuprelated entities in the database: graphs, calculated channels, regions of interest (ROIs), and test setups themselves (including hardware metadata and group associations). It acts as a bridge between higher-level application logic and SQL Server stored procedures, enforcing user authentication, validating input parameters, and translating database errors into standardized error codes. The module is part of the `DbAPI.TestSetups` namespace and is used to persist and retrieve configuration data for physical test setups (e.g., in data acquisition systems), supporting CRUD operations and domain-specific logic such as ROI generation defaults and test setup completeness flags.
## 2. Public Interface
All interfaces define *public* contracts; implementations are internal (`CalculatedChannels`, `Graphs`, `RegionsOfInterest`, `TestSetups` classes). Only interface methods are documented here.
### `IGraphs`
- **`ulong GraphsGet(IUserDbRecord user, IConnectionDetails connection, int? graphId, int? testSetupId, out IGraphRecord[] records)`**
Retrieves graph records matching optional `graphId` and `testSetupId`. Returns array of `IGraphRecord`.
- `graphId`: specific graph ID (nullable); `null` means all graphs for given `testSetupId`.
- `testSetupId`: required to filter graphs by test setup.
- **`ulong GraphsUpdate(IUserDbRecord user, IConnectionDetails connection, IGraphRecord record)`**
Updates an existing graph record in the database.
- Requires `record.GraphId` to be non-null and valid.
- **`ulong GraphsInsert(IUserDbRecord user, IConnectionDetails connection, ref IGraphRecord record)`**
Inserts a new graph record. After successful insert, `record.GraphId` is updated with the database-generated ID.
- **`ulong GraphsDelete(IUserDbRecord user, IConnectionDetails connection, int graphId)`**
Deletes the graph with the specified `graphId`.
### `ICalculatedChannels`
- **`ulong CalculatedChannelsDelete(IUserDbRecord user, IConnectionDetails connection, int calculatedChannelId)`**
Deletes the calculated channel with the specified `calculatedChannelId`.
- **`ulong CalculatedChannelsGet(IUserDbRecord user, IConnectionDetails connection, int? calculatedChannelId, string testSetupName, out ICalculatedChannelRecord[] records)`**
Retrieves calculated channel records matching optional `calculatedChannelId` and `testSetupName`.
- `testSetupName` may be empty string (`""`) to match no test setup.
- **`ulong CalculatedChannelsInsert(IUserDbRecord user, IConnectionDetails connection, ref ICalculatedChannelRecord record)`**
Inserts a new calculated channel. After successful insert, `record.Id` is updated with the database-generated ID.
- **`ulong CalculatedChannelsUpdate(IUserDbRecord user, IConnectionDetails connection, ICalculatedChannelRecord record)`**
Updates an existing calculated channel. Requires `record.Id` to be set.
### `IRegionsOfInterest`
- **`ulong RegionsOfInterestDelete(IUserDbRecord user, IConnectionDetails connection, int testSetupId)`**
Deletes *all* ROI records associated with the given `testSetupId` from both `TestSetupROIs` and `ROIPeriodChannels` tables.
- **`ulong RegionsOfInterestInsert(IUserDbRecord user, IConnectionDetails connection, int clientDbVersion, int testSetupId, IRegionOfInterest regionOfInterest)`**
Inserts ROI data into `TestSetupROIs` and `ROIPeriodChannels` tables.
- `clientDbVersion`: passed to stored procedure (likely for version-specific logic).
- **`ulong RegionsOfInterestGet(IUserDbRecord user, IConnectionDetails connection, int clientDbVersion, int testSetupId, out IRegionOfInterest[] records)`**
Retrieves all ROI records for the given `testSetupId`, combining data from `TestSetupROIs` and `ROIPeriodChannels`.
- **`ulong TestSetupROIsDelete(...)`**
Deletes only from `TestSetupROIs` table (not `ROIPeriodChannels`). Signature matches `RegionsOfInterestDelete`.
- **`ulong TestSetupROIsInsert(..., out int testSetupROIId)`**
Inserts into `TestSetupROIs` only; outputs the new `testSetupROIId`.
- **`ulong TestSetupROIsGet(..., out ITestSetupROIRecord[] records)`**
Retrieves only `TestSetupROIs` records (not joined with `ROIPeriodChannels`).
- **`ulong ROIPeriodChannelsInsert(...)`**
Inserts a single `ROIPeriodChannels` record (channel mapping for an ROI period).
- **`ulong ROIPeriodChannelsGet(..., out IROIPeriodChannelRecord[] roiPeriodChannelRecords)`**
Retrieves `ROIPeriodChannels` records for a given `testSetupROIId`.
### `ITestSetups`
- **`ulong TestSetupHardwareDelete(..., int? Id, int? dasId, int? testSetupId)`**
Deletes hardware metadata records. At least one of `Id`, `dasId`, or `testSetupId` must be non-null.
- **`ulong TestSetupHardwareUpdate(..., ITestSetupHardwareRecord record)`**
Updates a hardware metadata record.
- **`ulong TestSetupHardwareInsert(..., ITestSetupHardwareRecord record)`**
Inserts a hardware metadata record.
- **`ulong TestSetupHardwareGet(..., int clientDbVersion, int? testSetupId, out ITestSetupHardwareRecord[] records)`**
Retrieves hardware metadata (e.g., sample rate, anti-aliasing filter settings) for given `testSetupId` or all.
- **`ulong TestSetupGroupsInsert(..., ITestSetupGroupRecord record)`**
Inserts a grouptest setup association.
- **`ulong TestSetupGroupsUpdate(..., ITestSetupGroupRecord record)`**
Updates a grouptest setup association.
- **`ulong TestSetupGroupsGet(..., int? groupId, int? testSetupId, string testSetupName, out ITestSetupGroupRecord[] groups)`**
Retrieves grouptest setup associations matching criteria.
- **`ulong TestSetupsMarkIsDirtyIsComplete(..., string name, bool dirty, bool complete, string error)`**
Updates `IsDirty` and `IsComplete` flags for a test setup named `name`. `error` may contain validation messages.
- **`ulong TestSetupsDeleteByDate(..., DateTime date)`**
Deletes all test setups *older than* `date`.
- **`ulong TestSetupsAndGroupsDeleteAll(...)`**
Deletes *all* test setups and associated group associations.
- **`ulong TestSetupsDeleteById(..., int[] ids)`**
Deletes test setups by their IDs.
- **`ulong TestSetupsDeleteByName(..., string[] names)`**
Deletes test setups by their names.
- **`ulong TestSetupsUpdateInsert(..., int clientDbVersion, ref ITestSetupRecord record)`**
Inserts or updates a test setup record. `record` is passed by reference and may be modified (e.g., ID populated on insert).
- **`ulong TestSetupsGet(..., int clientDbVersion, int? testSetupId, string testSetupName, double defaultROIStart, double defaultROIEnd, bool defaultIgnoreShortedStart, bool defaultIgnoreShortedTrigger, out ITestSetupRecord[] records, out string[] errors)`**
Retrieves test setup records. Includes logic to generate default ROIs if missing (using `defaultROIStart`/`defaultROIEnd`).
- `defaultIgnoreShortedStart`/`defaultIgnoreShortedTrigger`: flags for backward compatibility with older DB versions (e.g., v91) that lack these fields.
## 3. Invariants
- **Authentication**: All methods first check `IsUserLoggedIn(user, connection)`; if false, return `ERROR_ACCESS_DENIED`.
- **Error Handling**: All methods return `ERROR_SUCCESS` (0) on success; non-zero values are error codes. Errors are logged via `LogManager`.
- **Null/Empty Handling**:
- Nullable parameters (`int?`, `string`) accept `null` or `""` (where applicable) to mean "no filter".
- `CalculatedChannelsGet` allows empty `testSetupName`.
- `GraphsGet` requires `testSetupId` to be non-null if filtering by test setup (but `graphId` may be null).
- **Input Validation**:
- `GraphsInsert`, `CalculatedChannelsInsert`, `CalculatedChannelsUpdate`, `CalculatedChannelsDelete` validate `record != null`.
- `TestSetupHardwareDelete` requires at least one ID parameter (`Id`, `dasId`, `testSetupId`) to be non-null.
- **Output Parameters**:
- `ref` parameters (`record` in `Insert` methods) are updated with database-generated IDs after successful insert.
- `out` parameters (`records`, `errors`) are always initialized (e.g., `records = new T[0]` on failure).
- **Resource Cleanup**: All methods dispose `SqlCommand` and connection in `finally` blocks.
## 4. Dependencies
### Module Dependencies
- **`DbAPI.Connections`**: Provides `IsUserLoggedIn`, `ConnectionManager.GetSqlCommand`.
- **`DbAPI.Errors`**: Defines `ErrorCodes.ERROR_*` constants.
- **`DbAPI.Logging`**: Provides `LogManager`.
- **`DTS.Common.Interface.*`**:
- `Database`: `IUserDbRecord`, `IConnectionDetails`.
- `Graphs`: `IGraphRecord`, `GraphRecord`.
- `TestSetups`: `ICalculatedChannelRecord`, `ITestSetupRecord`, `ITestSetupHardwareRecord`, `ITestSetupGroupRecord`, `IRegionOfInterest`, `ITestSetupROIRecord`, `IROIPeriodChannelRecord`.
- `RegionOfInterest`: `IRegionOfInterest`.
- **`System.Data.SqlClient`**: Used for `SqlCommand`, `SqlDbType`, `SqlParameter`.
- **`System.Globalization`**: Used for `CultureInfo.InvariantCulture.TextInfo.ListSeparator` (in `CalculatedChannels`).
### Inferred Usage
- Higher-level services (e.g., test setup management UI/backend) depend on these interfaces.
- The `TestSetups` class (not shown) likely orchestrates calls to `IGraphs`, `ICalculatedChannels`, `IRegionsOfInterest`, and `ITestSetups`.
## 5. Gotchas
- **`GraphsDelete` bug**: In `Graphs.cs`, `GraphsDelete` passes `@TestSetupId = null` to `sp_TestGraphsDelete` *regardless* of input. This likely ignores the `testSetupId` parameter from the interface and may delete graphs incorrectly (only filters by `@GraphId`).
*Observed in source: `cmd.Parameters.Add(new SqlParameter("@TestSetupId", SqlDbType.Int) { Value = null });`*
- **Logging inconsistency**: In `CalculatedChannelsGet`, errors are logged under `LogManager.LogEvents.Graphs` instead of `LogManager.LogEvents.CalculatedChannels`.
*Likely copy-paste error.*
- **`TestSetupROIsInsert` vs `RegionsOfInterestInsert`**: `RegionsOfInterestInsert` inserts into *both* `TestSetupROIs` and `ROIPeriodChannels`, while `TestSetupROIsInsert` inserts only into `TestSetupROIs`. Confusing naming—`RegionsOfInterest*` implies full ROI handling, but `TestSetupROIs*` is a partial subset.
- **`TestSetupsGet` side effects**: The `defaultROIStart`/`defaultROIEnd` parameters suggest the method may *generate* default ROIs on retrieval (not just read them). This could cause unexpected behavior if callers expect pure read-only behavior.
- **`CalculatedChannelsInsert`/`Update` input encoding**: `InputChannelIds` is converted to `byte[]` using `Utility.GetBytesFromStringArray(..., ListSeparator)`. The separator is hardcoded to `CultureInfo.InvariantCulture.TextInfo.ListSeparator` (typically `","`), but if `record.InputChannelIds` contains commas, parsing may fail. No validation or escaping is visible.
- **Missing `clientDbVersion` in `Graphs*` methods**: Unlike `RegionsOfInterest` and `TestSetups`, the `Graphs` methods do not accept a `clientDbVersion` parameter, suggesting version-specific logic is not supported for graphs.
- **No transaction support**: All operations are executed individually. If a logical operation requires multiple DB calls (e.g., insert ROI + period channels), callers must manage transactions externally.
- **No explicit validation of `testSetupId` existence**: Methods like `RegionsOfInterestInsert` accept `testSetupId` but do not verify it exists in the database before inserting. This may cause FK violations if the test setup is missing.

View File

@@ -0,0 +1,68 @@
---
source_files:
- DataPRO/DbAPI/User/User.cs
generated_at: "2026-04-16T04:25:44.390617+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "e2da1167bdd7ae60"
---
# User
## Documentation: `DbAPI.User.User` Class
### 1. Purpose
This module provides a concrete, internal implementation of the `IUserDbRecord` interface (which itself implements `IUser`) for representing user records retrieved from the database. It encapsulates user data fields (e.g., ID, credentials, role, metadata) and exposes a static factory method (`GetUser`) to fetch a user record by username via stored procedures. Its role is to act as a data carrier between the database layer and higher-level authentication or user management logic, ensuring structured access to user information while abstracting SQL interaction details.
### 2. Public Interface
*Note: The class is `internal`, so its members are not part of the public API surface of the `DbAPI.User` namespace, but they are documented per requirements.*
- **`User` Constructor**
```csharp
internal User(int id, string user, string display, string pwd, short role, DateTime lastModified, string lastModifiedBy, bool local)
```
Initializes a new `User` instance with all required field values. Parameters map directly to the public properties.
- **`GetUser` Static Method**
```csharp
internal static ulong GetUser(IConnectionDetails connection, out IUserDbRecord usr, string userName)
```
Retrieves a user record from the database by `userName`. Executes two stored procedures sequentially:
1. `sp_UsersGetId` to resolve the user ID from the username (output parameter `@UserId`).
2. `sp_UsersGet` to fetch full user details using the resolved ID.
On success, populates `usr` with a new `User` instance and returns `ERROR_SUCCESS` (`0`). On failure, returns an error code (e.g., `ERROR_LOGINFAILED`, `ERROR_ACCESS_DENIED`, `ERROR_UNKNOWN`) and sets `usr` to `null`.
**Properties of `usr` are populated using `Utility` helper methods** (e.g., `Utility.GetInt`, `Utility.GetString`, etc.), which handle `DBNull` and type conversion.
### 3. Invariants
- **`ID` must be a positive integer** (validated in `GetUser`: `if (0 >= id) { return ERROR_LOGINFAILED; }`).
- **`UserName`, `DisplayName`, `Password`, `LastModifiedBy` must be non-null strings** (assigned via `Utility.GetString`, which likely returns `string.Empty` on `DBNull`; source does not confirm null-safety, but assignment occurs unconditionally).
- **`LastModified` defaults to `DateTime.MinValue` if `DBNull`** (handled by `Utility.GetDateTime(..., DateTime.MinValue)`).
- **`Role` is a `short`** (no explicit range validation in code; assumed to be domain-constrained by application logic).
- **`LocalOnly` is a boolean** (assigned via `Utility.GetBool`, implying strict `true`/`false` mapping).
- **Database connection is disposed after use** (ensured by `finally { cmd.Connection.Dispose(); }`).
- **Stored procedure names are fixed**: `sp_UsersGetId` (ID lookup), `sp_UsersGet` (full record fetch).
### 4. Dependencies
- **Imports/Usings**:
- `DbAPI.Connections` → Provides `IConnectionDetails`, `ConnectionManager`, and `Errors.ErrorCodes`.
- `DTS.Common.Classes` → Likely contains `Utility` (used for type-safe data extraction).
- `DTS.Common.Interface.Database` → Defines `IUserDbRecord` (implemented by `User`).
- `System.Data`, `System.Data.SqlClient` → For `SqlDbType`, `CommandType`, `SqlParameter`, `IDataReader`.
- **External Dependencies**:
- SQL Server database with stored procedures: `sp_UsersGetId` (input: `@UserName NVARCHAR(255)`, output: `@UserId INT`) and `sp_UsersGet` (input: `@UserId INT`).
- `ConnectionManager.GetSqlCommand` must return `ERROR_SUCCESS` for valid connections.
- **Depended Upon**:
- `IUserDbRecord` interface (consumed by callers of `GetUser`).
- Likely used by authentication modules (e.g., login workflows) that require user record resolution.
### 5. Gotchas
- **Password stored in plaintext**: The `Password` property is directly assigned from the database (`var password = Utility.GetString(reader, "password");`). No hashing or encryption handling is evident—this may indicate legacy security practices.
- **Two-step database round-trips**: `GetUser` executes *two* stored procedures (ID lookup → full record fetch), which could be optimized into one query.
- **`reader.Close()` before reusing `cmd`**: The `SqlDataReader` is closed before resetting `cmd.CommandText` and parameters. This is safe but non-idiomatic; `using` blocks would improve robustness.
- **No validation of `userName` input**: Empty or null `userName` may cause unexpected behavior (e.g., SQL injection risk if `ConnectionManager` does not sanitize parameters).
- **`ERROR_ACCESS_DENIED` returned for `GetSqlCommand` failure**: This error code is misleading—it suggests authentication failure, but it may also indicate connection configuration issues.
- **`IUserDbRecord` interface is not defined here**: Behavior of `IUser`/`IUserDbRecord` members (e.g., `ID`, `UserName`) is assumed from implementation but not verified in this file.
- **`Utility` methods behavior is inferred**: Assumptions about `Utility.GetString` returning `string.Empty` on `DBNull` are not confirmed by this source.
*None identified from source alone.*

View File

@@ -0,0 +1,34 @@
---
source_files:
- DataPRO/DbAPI/obj/x86/Debug/net48/DbAPI.AssemblyInfo.cs
generated_at: "2026-04-16T04:27:10.344393+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "3e325bb6ccfbb741"
---
# net48
## 1. Purpose
This file is an auto-generated assembly metadata file (`DbAPI.AssemblyInfo.cs`) produced by the MSBuild toolchain during the build of the `DbAPI` .NET Framework 4.8 class library. It does not contain application logic or runtime behavior; its sole purpose is to embed assembly-level attributes (e.g., version, company, title) into the compiled binary. It exists to satisfy .NET assembly metadata requirements and is not intended for manual modification or direct consumption by application code.
## 2. Public Interface
**No public types, functions, classes, or methods are defined in this file.**
The file exclusively contains `assembly:`-prefixed attributes (e.g., `[assembly: AssemblyCompany(...)]`). These are compile-time metadata directives, not executable code, and do not expose any runtime API surface.
## 3. Invariants
- The file is **strictly read-only** in practice: manual edits will be overwritten on subsequent builds (as noted in the `<auto-generated>` header).
- All attributes are emitted at assembly level and apply to the entire `DbAPI` assembly.
- Versioning is fixed to `1.0.0.0` for both `AssemblyVersion` and `AssemblyFileVersion`; `AssemblyInformationalVersion` is `"1.0.0"` (a string, allowing non-numeric suffixes).
- Configuration is hardcoded to `"Debug"`; this reflects the build configuration at generation time.
## 4. Dependencies
- **Build-time dependency**: MSBuild (specifically the `WriteCodeFragment` task) and the .NET Framework 4.8 SDK.
- **Runtime dependency**: None — this file contributes only metadata and has no runtime impact.
- **Consumers**: None directly; the compiled assembly (`DbAPI.dll`) may be referenced by other projects, but this file itself is not referenced or used at runtime.
## 5. Gotchas
- **Do not edit manually**: Changes will be lost on rebuild. Versioning or metadata changes must be made in the project file (e.g., `.csproj`) or via `AssemblyInfo.cs` in the projects `Properties/` folder (if not auto-generated there).
- The `"1.0.0.0"` version may cause conflicts if multiple builds overwrite each other without version incrementing; ensure versioning strategy is managed at the project level.
- The `"Debug"` configuration is baked into metadata — this is unusual for production builds and may indicate a misconfigured release pipeline if present in deployed artifacts.
- None identified from source alone.