Database Schema Documentation¶
Overview¶
The Essert.MF system uses 8 separate MySQL/MariaDB databases to organize data by domain:
- db_productparameter - Product parameter configurations (63 tables)
- db_process - Manufacturing process data (1 table)
- db_statistics - Measurement and statistics data (23 tables)
- db_changelogs - Historical change tracking (70 tables)
- db_essert - Core system configuration (3 tables)
- db_robots - Robot control and positioning (5 tables)
- db_wpc - Work Piece Carrier management (4 tables)
- db_systemparameter - System-level machine parameters (8 tables)
⚠️ CRITICAL: UID AUTO_INCREMENT Pattern¶
Database Constraint¶
ALL primary key UID columns across ALL databases use AUTO_INCREMENT.
This is a database-level constraint that must be respected in all repository code.
-- Example from tbl_th_process
CREATE TABLE `tbl_th_process` (
`UID` bigint(20) NOT NULL AUTO_INCREMENT,
`ProcessName` varchar(45) NOT NULL DEFAULT '',
`Timestamp` datetime NOT NULL DEFAULT '2022-01-01 12:00:00',
`Creator` varchar(30) NOT NULL DEFAULT '',
`CRC` bigint(20) NOT NULL DEFAULT 0,
PRIMARY KEY (`UID`),
UNIQUE KEY `UID_UNIQUE` (`UID`)
) ENGINE=InnoDB AUTO_INCREMENT=95;
Affected Tables (All Databases)¶
db_productparameter: (63 tables total)
- TH (TrayHandling): tbl_th_process, tbl_th_mapping, tbl_th_shape, tbl_th_positiondata, tbl_th_shiftdata, tbl_th_mapping_positiondata, tbl_th_mapping_shiftdata
- SC (Steep Conveyor): tbl_sc_setup, tbl_sc_mapping
- EGP (Schunk EGP Gripper): tbl_egp_setup, tbl_egp_mapping, tbl_egp_movements, tbl_egp_positionname
- EGK (Schunk EGK Gripper): tbl_egk_setup, tbl_egk_mapping, tbl_egk_movements, tbl_egk_positionname
- RFL (Refill Logic): tbl_rfl_setup, tbl_rfl_mapping
- XGX (Keyence XGX Vision): tbl_xgx_setup, tbl_xgx_mapping
- IV3 (Keyence IV3 Vision): tbl_iv3_setup, tbl_iv3_mapping
- Kann (KocoMotion Stepper): tbl_kann_setup, tbl_kann_mapping, tbl_kann_movements, tbl_kann_positionname
- HP (RNA Vibration Hopper): tbl_hp_setup, tbl_hp_mapping
- ES (Essert Shaker): tbl_es_clips, tbl_es_mapping, tbl_es_sequences
- VS (Vision Sequences): tbl_vs_clips, tbl_vs_mapping, tbl_vs_sequences
- CMMT (FESTO Servo CMMT-AS): tbl_cmmt_setup, tbl_cmmt_mapping, tbl_cmmt_movements, tbl_cmmt_positionname
- CL (Circulation Logic): tbl_cl_setup, tbl_cl_mapping
- EL (Essert Lighting): tbl_el_setup, tbl_el_mapping
- Lighting: tbl_lighting_setup, tbl_lighting_mapping
- GJ (Gripperjaws): tbl_gj_setup, tbl_gj_mapping
- DistanceSensor: tbl_distancesensor_setup, tbl_distancesensor_mapping, tbl_distancesensor_distances, tbl_distancesensor_distancename
- Pickit3D (3D Vision): tbl_pickit3d_setup, tbl_pickit3d_mapping
- SchmalzSCPSi (Vacuum): tbl_schmalzscpsi_setup, tbl_schmalzscpsi_mapping
- MFConfig: tbl_mfconfig_config, tbl_mfconfig_mapping, tbl_mfconfig_setup
- MFProcess: tbl_mfprocess_mapping
- Process: tbl_process_mapping
- Product: tbl_productnames, tbl_versions
- Unspecified: tbl_unspecified
db_wpc: (4 tables total)
- tbl_wpc, tbl_mapping_wpc, tbl_mapping_version, tbl_groups
db_statistics: (23 tables total)
- Measurement tables: tbl_camera, tbl_cmmt, tbl_consumption, tbl_cycletime, tbl_egp, tbl_force, tbl_kann, tbl_length, tbl_posunit, tbl_pressure, tbl_shock, tbl_temperature, tbl_valve, tbl_weight
- Message/tracking tables: tbl_currentmessages, tbl_listignoremessages, tbl_listmessageclass, tbl_listmessages, tbl_listnokreasons, tbl_messagearchive
- List tables: tbl_listordernumbers, tbl_listserialnumbers, tbl_listprocesses
db_robots: (5 tables total)
- tbl_limits, tbl_listcoordinatesystems, tbl_listtools, tbl_masterdata, tbl_positiondata
db_essert: (3 tables total)
- tbl_arwid, tbl_variables, tbl_version
db_process: (1 table)
- tbl_processdata
db_changelogs: (70 tables total)
- Product parameter changelogs (tbl_pp_*): Mirrors of all db_productparameter tables with tbl_pp_ prefix (57 tables)
- Robot changelogs: tbl_robots_limits, tbl_robots_masterdata, tbl_robots_positiondata
- System parameter changelogs (tbl_sp_*): tbl_sp_assemblyunit, tbl_sp_cmmt, tbl_sp_posaxis, tbl_sp_positioningunit, tbl_sp_robots, tbl_sp_traystacker, tbl_sp_unspecified, tbl_sp_wpcopener
- WPC changelogs: tbl_wpc_mapping_version, tbl_wpc_mapping_wpc, tbl_wpc_wpc
db_systemparameter: (8 tables total) - System-level machine configuration
- tbl_assemblyunit - Assembly unit parameters (reference direction, port, end position, speed/force limits)
- tbl_cmmt - FESTO CMMT servo system parameters (scaling, position limits, speeds, reference offset)
- tbl_posaxis - Positioning axis parameters (scaling, position limits, speeds, reference offset)
- tbl_positioningunit - Positioning unit parameters (open/stop/index positions, velocities, holding torques)
- tbl_robots - Robot speed parameters (fast/slow speeds)
- tbl_traystacker - Tray stacker parameters (stack height limits, tray count, cart level correction)
- tbl_unspecified - Dynamic project-specific system parameters
- tbl_wpcopener - WPC opener parameters (open/release/pre-positions, tolerance)
Essentially: If a table has a UID column, it uses AUTO_INCREMENT.
Correct Implementation Pattern¶
✅ CORRECT: Let Database Generate UID¶
public virtual async Task<long> CreateSetupWithCrcAsync(TSetupDto setup)
{
var setupEntity = Mapper.Map<TSetup>(setup);
// Set Uid = 0 or leave default (database ignores and generates new UID)
var entry = ProductContext.Entry(setupEntity);
entry.Property("Timestamp").CurrentValue = DateTime.UtcNow;
// Phase 1: Insert to trigger AUTO_INCREMENT
SetupDbSet.Add(setupEntity);
await ProductContext.SaveChangesAsync();
// Phase 2: Read back the auto-generated UID
var uid = (long)entry.Property("Uid").CurrentValue;
// Phase 3: Calculate CRC with actual UID
var crc = CalculateSetupCrc(setupEntity);
entry.Property("Crc").CurrentValue = (long)crc;
await ProductContext.SaveChangesAsync();
return uid;
}
❌ WRONG: Manual UID Calculation¶
// DO NOT DO THIS - Has race conditions!
public virtual async Task<long> CreateSetupWithCrcAsync(TSetupDto setup)
{
// ❌ Race condition: Two concurrent requests can get same maxUid
var maxUid = await SetupDbSet.MaxAsync(s => (long?)s.Uid) ?? 0;
var newUid = maxUid + 1;
setupEntity.Uid = newUid; // ❌ Fights against AUTO_INCREMENT
SetupDbSet.Add(setupEntity);
await ProductContext.SaveChangesAsync();
return newUid; // ❌ May not match actual database UID if AUTO_INCREMENT counter is different
}
Why Manual UID Calculation Fails¶
- Race Condition: Between
MaxAsync()andSaveChangesAsync(), another request can insert with the same UID - AUTO_INCREMENT Counter Desync: Database has its own counter that may be ahead of MAX(UID) if rows were deleted
- Performance: Extra
MAX(UID)query adds latency to every insert - Duplicate Key Errors: Database rejects insert if manually-set UID conflicts
- Complexity: Unnecessary code that duplicates built-in database functionality
Why Two-Phase Insert is Required¶
CRC calculation includes the UID in the hash, so we can't calculate CRC before knowing the UID:
// CRC calculation example (from ThMappingRepository)
protected override uint CalculateSetupCrc(ThProcess process)
{
var content = CrcService.BuildCrcContent("EsrtRbtx-",
process.Uid, // ← UID is part of CRC calculation
process.ProcessName,
process.RowMirroring,
// ... other fields
process.Timestamp,
process.Creator);
return CrcService.CalculateCrc32(content);
}
Process: 1. Insert without CRC → Database generates UID 2. Read UID back from entity 3. Calculate CRC using actual UID 4. Update CRC field
Benefits of AUTO_INCREMENT¶
✅ Thread-Safe: Database handles concurrency automatically ✅ Performant: No extra MAX query needed ✅ Reliable: Single source of truth for UID generation ✅ Simple: Less code, fewer bugs ✅ Atomic: UID generation and insert happen in one database operation
Common Column Patterns¶
All tables follow similar patterns:
`UID` bigint(20) NOT NULL AUTO_INCREMENT -- Primary key (auto-generated)
`Timestamp` datetime NOT NULL -- Created/modified timestamp
`Creator` varchar(30) NOT NULL DEFAULT '' -- User/system that created record
`CRC` bigint(20) NOT NULL DEFAULT 0 -- Data integrity checksum
CRC Calculation Pattern¶
CRC is calculated AFTER UID is known and includes: - UID (auto-generated) - All business data fields - Timestamp - Creator
This ensures data integrity and tamper detection.
Testing Considerations¶
When writing tests: - ✅ Let repository create entities (UIDs auto-generated) - ✅ Use actual database for integration tests - ❌ Don't mock UID generation - ❌ Don't hardcode expected UIDs
// Good: Let repository handle UID
var setupUid = await repository.CreateSetupWithCrcAsync(setupDto);
Assert.That(setupUid > 0); // Verify UID was generated
// Bad: Hardcoded UID expectations
var setupUid = await repository.CreateSetupWithCrcAsync(setupDto);
Assert.That(setupUid == 42); // ❌ Fragile - depends on database state
References¶
- Implementation:
Essert.MF.Infrastructure/Repositories/ParameterMappingRepository.cs - Architecture:
CLAUDE.md- "UID AUTO_INCREMENT Pattern" section - Testing:
Essert.MF.API.Rest.Tests/Controllers/*ControllerTests.cs
History¶
2025-01-10: Refactored from manual UID calculation to AUTO_INCREMENT pattern - Fixed race conditions in concurrent requests - Improved performance (removed MAX query) - Simplified repository code - All 19 TH parameter tests passing with new approach