Skip to content

Commit b75d378

Browse files
committed
v3.13.2: Config writes set full PDM metadata, auto-refresh preserves DB values
1 parent dcf1f47 commit b75d378

File tree

7 files changed

+251
-199
lines changed

7 files changed

+251
-199
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# RCA: Auto-refresh Overwrites Part Number with Stale File-Level Property
2+
3+
## Status: FIXED
4+
5+
## Summary
6+
7+
When a user edits a SolidWorks file and saves (e.g., changing "use description in BOM" checkbox), the FileWatcher triggers an auto-refresh that reads metadata from the file. For files where "Save to File" was never used, stale legacy file-level properties (like BR-100115) would overwrite the correct BluePLM database values (like BR-107163).
8+
9+
## Root Cause Chain
10+
11+
1. `WLP-BULKHEAD-JPT.SLDPRT` created long ago with file-level `Number = "BR-100115"` (legacy)
12+
2. BluePLM imported file with correct part number **BR-107163** in database
13+
3. **"Save to File" was never run** - SW file properties never updated to match DB
14+
4. File properties remained stale: `Number = BR-100115` while DB has `part_number = BR-107163`
15+
5. User edits file in SolidWorks (changes "use description in BOM")
16+
6. FileWatcher detects file change, triggers `refreshMetadataForFiles()`
17+
7. Auto-refresh reads stale `Number = BR-100115` from file-level properties
18+
8. Auto-refresh **overwrites** correct pendingMetadata with stale value
19+
20+
## The Fix
21+
22+
Modified `refreshMetadataForFiles()` in [`src/lib/commands/handlers/syncMetadata.ts`](../../src/lib/commands/handlers/syncMetadata.ts) to only refresh `revision` for parts/assemblies.
23+
24+
**Before:** Auto-refresh updated `part_number`, `tab_number`, `description`, and `revision` from file
25+
**After:** Auto-refresh only updates `revision` for parts/assemblies
26+
27+
This aligns with the documented design:
28+
> "For PARTS/ASSEMBLIES (.sldprt/.sldasm): PUSH - BluePLM is the source of truth for part/assembly metadata"
29+
30+
## Why BluePLM Didn't Write to the File
31+
32+
BluePLM only writes to SW files when explicitly triggered via "Save to File" button. It doesn't automatically sync DB → file. This is by design (avoids modifying files without user consent), but means legacy files can have stale properties until the user runs "Save to File".
33+
34+
## Files Modified
35+
36+
- `src/lib/commands/handlers/syncMetadata.ts` - Lines 628-670, removed `part_number`, `tab_number`, `description` from auto-refresh for parts/assemblies
37+
38+
## Testing
39+
40+
1. Edit a multi-config SW file in SolidWorks (e.g., change "use description in BOM")
41+
2. Save the file
42+
3. Verify in BluePLM that the part number is NOT overwritten with file-level values
43+
4. Verify revision IS updated if changed in file
Lines changed: 81 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -1,164 +1,112 @@
11
---
2-
name: fix-drawing-sync-metadata
3-
overview: Write config description and tab number to SW file immediately on edit commit (blur/Enter), so sync-metadata always reads fresh data.
2+
name: Fix Drawing Sync Metadata
3+
overview: "Fix two bugs: (1) Response mismatch causing drawing sync to receive empty parent properties, and (2) SW API loading assembly components when it should use DM API instead."
44
todos:
5-
- id: write-config-desc-immediately
6-
content: Modify handleConfigDescriptionChange to write description to SW file immediately via setProperties
5+
- id: fix-api-selection
6+
content: Change GetPropertiesFast to only use SW API when the specific file is open in SW, otherwise use DM API
77
status: pending
8-
- id: write-config-tab-immediately
9-
content: Modify handleConfigTabChange to write tab number to SW file immediately via setProperties
8+
- id: fix-response-id
9+
content: Ensure all C# CommandResult responses include the requestId for proper matching
1010
status: pending
11-
- id: test-sync-metadata
12-
content: Test that editing config description/tab writes to SW file immediately, and sync-metadata on drawing reads the updated values
11+
- id: verify-fifo-fallback
12+
content: Review FIFO fallback logic in solidworks.ts to understand when mismatches occur
1313
status: pending
1414
---
1515

16-
# Fix Config Description Not Syncing to Drawing
16+
# Fix Drawing Sync Metadata Bugs
1717

18-
## Problem Analysis
18+
## Problem 1: Drawing Sync Gets Empty Parent Properties
1919

20-
When a user edits a description on a configuration row of a part/assembly in BluePLM, the edit is stored only in memory (`pendingMetadata.config_descriptions`). When the user then runs "Sync Metadata" on a drawing that references that part's configuration, the command reads from the **disk file** (via `getProperties`) which has the OLD description.
20+
The log shows that when syncing metadata on a drawing, the `getProperties` call for the parent model returns empty data even though the SW service successfully reads the properties.
2121

22-
**Current Flow** (broken):
22+
**Evidence from log:**
2323

24-
1. User edits description on config row of `stator.SLDPRT` - stored ONLY in memory
25-
2. User right-clicks drawing `stator.SLDDRW`, clicks "Sync Metadata"
26-
3. `pullDrawingMetadata()` reads parent model properties from disk via `getProperties()`
27-
4. Disk still has old description - sync-metadata pulls stale data to drawing
24+
- Line 980: `getProperties (id: 48) completed in 2ms` - impossibly fast
25+
- Lines 997-1046: SW Service logs reading `BR-107166` from file
26+
- Line 1063: Electron receives `filePropertyCount: 0`
2827

29-
**Why "Generate BR Number" works**: It calls `saveConfigsToSWFile()` which writes ALL pending metadata (including `config_descriptions`) to the SW file, so sync-metadata reads the updated value.
28+
**Root Cause:** Response mismatch in the IPC layer. Looking at [solidworks.ts](electron/handlers/solidworks.ts) lines 836-856:
3029

31-
## Solution
30+
- If `requestId` in response doesn't match pending requests, it falls back to FIFO matching
31+
- A ping response without requestId gets matched to the wrong request
3232

33-
**Write config description to SW file immediately** when the user commits the edit (blur or Enter). This keeps the SW file as the single source of truth and ensures sync-metadata always reads fresh data.
33+
**Fix:** Ensure all C# service responses include `requestId` for proper matching.
3434

35-
## Implementation
35+
## Problem 2: Component Files (WireAssembly.SLDPRT) Opening in SolidWorks
3636

37-
### File: [src/features/source/browser/hooks/useConfigHandlers.ts](src/features/source/browser/hooks/useConfigHandlers.ts)
38-
39-
#### 1. Modify `handleConfigDescriptionChange` (lines 273-292)
40-
41-
Add immediate write to SW file via `setProperties`:
42-
43-
```typescript
44-
const handleConfigDescriptionChange = useCallback(async (filePath: string, configName: string, value: string) => {
45-
const { files, fileConfigurations } = usePDMStore.getState()
46-
47-
const file = files.find(f => f.path === filePath)
48-
if (!file) return
49-
50-
// Update config in store (for immediate UI feedback)
51-
const configs = fileConfigurations.get(filePath)
52-
if (configs) {
53-
const updated = configs.map(c => c.name === configName ? { ...c, description: value } : c)
54-
usePDMStore.getState().setFileConfigurations(filePath, updated)
55-
}
56-
57-
// Update pending metadata (for persistence across app restart)
58-
const existingDescs = file.pendingMetadata?.config_descriptions || {}
59-
usePDMStore.getState().updatePendingMetadata(filePath, {
60-
config_descriptions: { ...existingDescs, [configName]: value }
61-
})
62-
63-
// Write to SW file immediately
64-
try {
65-
const result = await window.electronAPI?.solidworks?.setProperties(filePath, { 'Description': value }, configName)
66-
if (result?.success) {
67-
addToast('success', `Saved description to ${configName}`)
68-
} else {
69-
addToast('error', `Failed to save description: ${result?.error || 'Unknown error'}`)
70-
}
71-
} catch (err) {
72-
log.error('[ConfigHandlers]', 'Failed to write config description to SW file', { filePath, configName, error: err })
73-
addToast('error', 'Failed to save description to file')
74-
}
75-
}, [addToast])
37+
**Confirmed:** WireAssembly.SLDPRT is a component inside BAR-XT-ASM.SLDASM. It was NOT open before sync metadata.
38+
39+
**Timeline from log:**
40+
41+
- Line 715 (22:18:34.260): `No documents open in SolidWorks`
42+
- Line 883 (22:18:37.687): `Open doc: WireAssembly.SLDPRT` - appeared during sync!
43+
44+
**Root Cause:** In [Program.cs](solidworks-service/BluePLM.SolidWorksService/Program.cs) lines 426-429:
45+
46+
```csharp
47+
// If SolidWorks is RUNNING (even with other files open), use SW API for consistency
48+
if (_swApi != null && _swApi.IsSolidWorksRunning())
49+
{
50+
return _swApi.GetCustomProperties(filePath, ...);
51+
}
7652
```
7753

78-
#### 2. Modify `handleConfigTabChange` (lines 247-266)
79-
80-
Add immediate write to SW file via `setProperties`. Also need to recalculate the full `Number` property (base + tab):
81-
82-
```typescript
83-
const handleConfigTabChange = useCallback(async (filePath: string, configName: string, value: string) => {
84-
const { files, fileConfigurations } = usePDMStore.getState()
85-
86-
const file = files.find(f => f.path === filePath)
87-
if (!file) return
88-
89-
const upperValue = value.toUpperCase()
90-
91-
// Update config in store
92-
const configs = fileConfigurations.get(filePath)
93-
if (configs) {
94-
const updated = configs.map(c => c.name === configName ? { ...c, tabNumber: upperValue } : c)
95-
usePDMStore.getState().setFileConfigurations(filePath, updated)
96-
}
97-
98-
// Update pending metadata
99-
const existingTabs = file.pendingMetadata?.config_tabs || {}
100-
usePDMStore.getState().updatePendingMetadata(filePath, {
101-
config_tabs: { ...existingTabs, [configName]: upperValue }
102-
})
103-
104-
// Write to SW file immediately
105-
try {
106-
const baseNumber = file.pendingMetadata?.part_number ?? file.pdmData?.part_number ?? ''
107-
const props: Record<string, string> = { 'Tab Number': upperValue }
108-
109-
// Also update the full Number property (base + tab)
110-
if (baseNumber && upperValue) {
111-
props['Number'] = `${baseNumber}-${upperValue}` // TODO: use serialization settings for separator
54+
This is **overly aggressive** - it uses the full SW API whenever SolidWorks is running, even if the target file isn't open. When SW API opens an assembly via `OpenDoc6`:
55+
56+
1. SolidWorks loads ALL component references (like WireAssembly.SLDPRT)
57+
2. `CloseDoc` only closes the main assembly
58+
3. **Component files remain orphaned** in SolidWorks session
59+
60+
The **Document Manager API** can read properties without loading files into SolidWorks at all!
61+
62+
## Implementation
63+
64+
### Step 1: Fix API Selection Logic (PRIMARY FIX)
65+
66+
In [Program.cs](solidworks-service/BluePLM.SolidWorksService/Program.cs) `GetPropertiesFast`, change the logic to:
67+
68+
- Only use SW API if the **specific file** is already open in SolidWorks
69+
- Otherwise use DM API (fast, doesn't load files into SW)
70+
```csharp
71+
static CommandResult GetPropertiesFast(string? filePath, JObject command)
72+
{
73+
// ONLY use SW API if THIS SPECIFIC FILE is already open in SolidWorks
74+
// This prevents loading component files into SW when reading assembly properties
75+
if (_swApi != null && !string.IsNullOrEmpty(filePath) && _swApi.IsFileOpenInSolidWorks(filePath))
76+
{
77+
Console.Error.WriteLine($"[Service] File is open in SolidWorks, using SW API: {Path.GetFileName(filePath)}");
78+
return _swApi.GetCustomProperties(filePath, command["configuration"]?.ToString());
11279
}
11380

114-
const result = await window.electronAPI?.solidworks?.setProperties(filePath, props, configName)
115-
if (result?.success) {
116-
addToast('success', `Saved tab number to ${configName}`)
117-
} else {
118-
addToast('error', `Failed to save tab number: ${result?.error || 'Unknown error'}`)
81+
// Use Document Manager API - fast and doesn't load files into SW
82+
if (_dmApi == null)
83+
{
84+
return new CommandResult { Success = false, Error = "Document Manager not available" };
11985
}
120-
} catch (err) {
121-
log.error('[ConfigHandlers]', 'Failed to write config tab to SW file', { filePath, configName, error: err })
122-
addToast('error', 'Failed to save tab number to file')
123-
}
124-
}, [addToast])
86+
87+
Console.Error.WriteLine($"[Service] Using Document Manager API for: {Path.GetFileName(filePath)}");
88+
return _dmApi.GetCustomProperties(filePath, command["configuration"]?.ToString());
89+
}
12590
```
12691

127-
### Key Changes
12892

129-
1. Make both functions `async`
130-
2. After updating the store, call `setProperties()` to write to the SW file's configuration
131-
3. Show toast on success ("Saved to file") and on failure ("Failed to save to file")
132-
4. If the write fails, the pending metadata is still saved and will sync on check-in
93+
**Important:** Remove the `IsSolidWorksRunning()` check that forces SW API usage.
13394

134-
### Note on Tab Number
95+
### Step 2: Fix Response ID Matching
13596

136-
For tab number, we also need to update the `Number` property (combined base + tab). The TODO notes that we should use serialization settings for the separator, but for now a simple dash works.
97+
In `Program.cs`, ensure `RequestId` is always set on the CommandResult before returning:
13798

138-
## Testing
139-
140-
### Test 1: Config Description
141-
142-
1. Open a part with configurations in BluePLM
143-
2. Check out the part
144-
3. Edit the description on one of its configuration rows (e.g., T200X)
145-
4. Click away from the input (blur) or press Enter
146-
5. Right-click on a drawing that references that configuration
147-
6. Click "Sync Metadata"
148-
7. **Expected**: Drawing's description should update to the new value immediately
149-
150-
### Test 2: Config Tab Number
99+
```csharp
100+
var result = action switch { ... };
101+
result.RequestId = requestId; // Always set before returning
102+
return result;
103+
```
151104

152-
1. Open a part with configurations in BluePLM
153-
2. Check out the part
154-
3. Edit the tab number on one of its configuration rows
155-
4. Click away from the input (blur) or press Enter
156-
5. Check the SW file properties (or right-click drawing > Sync Metadata)
157-
6. **Expected**: The Number property should show the updated base-tab combination
105+
### Step 3: Apply Same Fix to Other Fast Operations
158106

159-
### Verify SW File Updated
107+
Review and apply the same API selection logic to:
160108

161-
After editing, you can verify the SW file was updated by:
109+
- `GetConfigurationsFast`
110+
- `GetReferencesFast`
162111

163-
- Opening the part in SolidWorks and checking custom properties
164-
- Or running "Sync Metadata" on a drawing referencing that config
112+
These should also only use SW API when the specific file is open.

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22

33
All notable changes to BluePLM will be documented in this file.
44

5+
## [3.13.2] - 2026-02-05
6+
7+
### Improved
8+
- **Config property writes now set full PDM metadata**: Editing tab number or description on a configuration now writes all related properties in a single operation - Number (combined part number), Base Item Number, Tab Number, Description, Date, and DrawnBy. This ensures files are complete when used outside BluePLM
9+
10+
### Fixed
11+
- **Auto-refresh no longer overwrites part number and description**: For parts and assemblies, auto-refresh now only updates the revision from the file. BluePLM is the source of truth for part number, tab number, and description - reading these from the file would overwrite database values with potentially stale legacy properties
12+
- **SolidWorks no longer auto-launches for drawing sync**: Reading drawing references from the parent model now requires SolidWorks to already be running. The service no longer attempts to auto-start SolidWorks, which could create zombie processes and long hangs. Users see a clear message if SW needs to be opened
13+
14+
---
15+
516
## [3.13.1] - 2026-02-05
617

718
### Fixed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "blue-plm",
3-
"version": "3.13.1",
3+
"version": "3.13.2",
44
"description": "Open-source Product Lifecycle Management",
55
"main": "dist-electron/main.js",
66
"scripts": {

solidworks-service/BluePLM.SolidWorksService/Program.cs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -412,18 +412,18 @@ static CommandResult GetBomFast(string? filePath, JObject command)
412412

413413
static CommandResult GetPropertiesFast(string? filePath, JObject command)
414414
{
415-
// If SolidWorks has this file open, use full SW API to avoid DM API conflict
416-
// (DM API accessing a file open in SW can cause SW to close the file)
415+
// ONLY use SW API if THIS SPECIFIC FILE is already open in SolidWorks
416+
// This prevents loading component files into SW when reading assembly properties
417+
// (Opening an assembly via OpenDoc6 loads ALL component references, which stay orphaned
418+
// in SW session even after closing the main assembly)
417419
if (_swApi != null && !string.IsNullOrEmpty(filePath) && _swApi.IsFileOpenInSolidWorks(filePath))
418420
{
419421
Console.Error.WriteLine($"[Service] File is open in SolidWorks, using SW API: {Path.GetFileName(filePath)}");
420422
return _swApi.GetCustomProperties(filePath, command["configuration"]?.ToString());
421423
}
422424

423-
// ONLY use Document Manager API - NEVER fall back to full SolidWorks!
424-
// Opening SolidWorks just for property extraction can take 20-30+ seconds.
425-
// If Document Manager fails, the user can manually use "Refresh Metadata"
426-
// which intentionally uses full SW API.
425+
// Use Document Manager API - fast and doesn't load files into SolidWorks
426+
// DM API can read properties without launching SW or loading any component files
427427
// Note: We only check for null here. The DM methods internally call Initialize()
428428
// which handles reinitialization after ReleaseHandles() was called.
429429

@@ -563,14 +563,22 @@ static CommandResult GetReferencesFast(string? filePath)
563563
bool swRunning = swApiAvailable && _swApi!.IsSolidWorksRunning();
564564
Console.Error.WriteLine($"[Service-Fallback] refCount={refCount}, _swApi!=null={swApiAvailable}, swRunning={swRunning}");
565565

566-
// Auto-start SW for drawings - removed swRunning check to enable auto-launch
566+
// Only use SW API fallback if SolidWorks is already running
567+
// Don't auto-launch - it creates zombie processes and long hangs
567568
if (refCount == 0 && swApiAvailable)
568569
{
569570
if (!swRunning)
570571
{
571-
Console.Error.WriteLine($"[Service-Fallback] SolidWorks not running - will auto-start in background for drawing references");
572+
// Don't auto-launch - return message asking user to open SW
573+
Console.Error.WriteLine($"[Service-Fallback] SolidWorks not running - skipping fallback (user must open SW manually)");
574+
return new CommandResult
575+
{
576+
Success = false,
577+
Error = "SOLIDWORKS_NOT_RUNNING",
578+
Data = new { message = "SolidWorks must be running to read drawing references from parent model" }
579+
};
572580
}
573-
Console.Error.WriteLine($"[Service-Fallback] ALL CONDITIONS MET - Attempting SW API fallback: {Path.GetFileName(filePath)}");
581+
Console.Error.WriteLine($"[Service-Fallback] SW is running - Attempting SW API fallback: {Path.GetFileName(filePath)}");
574582
var swResult = _swApi!.GetExternalReferences(filePath);
575583
if (swResult.Success)
576584
{

0 commit comments

Comments
 (0)