Skip to content

Tool Calibration Architecture

Overview

The Tool Calibration system is a sophisticated quality management framework built on LoopBack 4, designed to ensure tools maintain their accuracy and compliance with industry standards. This document provides a technical deep-dive into the system's architecture, data models, business logic, and extension points.

System Architecture

High-Level Components

┌─────────────────────────────────────────────────────────────┐
│                    Angular Frontend                         │
│  ┌──────────────────┐  ┌──────────────────────────────┐    │
│  │ Calibration Forms│  │WidgetToolCalibrationDue      │    │
│  └────────┬─────────┘  └────────┬─────────────────────┘    │
│           │                      │                           │
└───────────┼──────────────────────┼───────────────────────────┘
            │                      │
            │ HTTP/REST            │ HTTP/REST
            │                      │
┌───────────▼──────────────────────▼───────────────────────────┐
│                    LoopBack 4 API                            │
│  ┌─────────────────────────────────────────────────────┐    │
│  │         ToolController.calibrateTool()              │    │
│  │                      │                               │    │
│  │                      ▼                               │    │
│  │         ToolRepository.processCalibration()         │    │
│  │                      │                               │    │
│  │         ┌────────────┴────────────┐                 │    │
│  │         ▼                          ▼                 │    │
│  │  Validation Logic        Calculation Engine         │    │
│  │  - Requirements          - Pull test status         │    │
│  │  - Tool config           - Overall status           │    │
│  │  - Data completeness     - Due date                 │    │
│  └─────────────────────────────────────────────────────┘    │
│                      │                                       │
└──────────────────────┼───────────────────────────────────────┘


         ┌─────────────────────────┐
         │      MySQL Database     │
         │  - tool                 │
         │  - tool_calibration     │
         │  - tool_calibration_test│
         │  - tool_calibration_*   │
         └─────────────────────────┘

Data Model

Entity Relationship Diagram

┌──────────────────┐
│   ToolType       │
│                  │
│  id              │────┐
│  name            │    │
│  description     │    │
│  calibration_    │    │
│    cycle         │    │
│  calibration_    │    │
│    multipiler    │    │
└──────────────────┘    │

                        │ tool_type_id

┌──────────────────┐    │     ┌────────────────────────────┐
│  Tool            │◄───┘     │ ToolCalibrationRequirement │
│                  │          │                            │
│  id              │          │  id                        │
│  name            │          │  tool_type_id ─────────────┤
│  tool_type_id    │          │  calibration_type_id       │
│  calibration_    │          │  calibration_standard_id   │
│    cycle         │          └──────────┬─────────────────┘
│  calibration_    │                     │
│    multipiler    │                     │
│  min_pull_force_n│                     │
│  max_pull_force_n│                     │
│  date_last_      │                     │
│    calibration   │                     │ calibration_type_id
│  date_          │                     │
│    calibration_  │                     │
│    due           │          ┌──────────▼─────────────────┐
│  last_          │          │  ToolCalibrationType       │
│    calibration_id├─────┐    │                            │
└──────────────────┘     │    │  id                        │
                         │    │  name                      │
                         │    │  description               │
                         │    │  controller (key field)    │
                         │    └────────────────────────────┘

                         │ last_calibration_id

    ┌────────────────────▼──────────────────┐
    │  ToolCalibration                      │
    │                                       │
    │  id                                   │
    │  tool_id                              │
    │  user_id                              │
    │  date_calibration                     │
    │  date_last_calibration                │
    │  date_calibration_due                 │
    │  status (1=pass, 0=fail)              │
    │  calibration_cycle                    │
    │  calibration_multipiler               │
    │  description                          │
    └───────────────┬───────────────────────┘

                    │ calibration_id

    ┌───────────────▼──────────────────────────┐
    │  ToolCalibrationTest                     │
    │                                          │
    │  id                                      │
    │  calibration_id                          │
    │  calibration_type_id                     │
    │  status (true=pass, false=fail)          │
    │  is_external                             │
    │  notes                                   │
    └──────┬──────────────────┬────────────────┘
           │                  │
           │                  │ calibration_test_id
           │                  │
           │      ┌───────────▼──────────────────────────┐
           │      │  ToolCalibrationTestPullSimple       │
           │      │                                      │
           │      │  id                                  │
           │      │  calibration_test_id                 │
           │      │  tensile_tool_id                     │
           │      │  part_id                             │
           │      │  tool_calibration_standard_id        │
           │      │  wire_awg_value                      │
           │      │  wire_mm_value                       │
           │      │  min_pull_force_n                    │
           │      │  pull_test_value_n (measured)        │
           │      │  status (true=pass, false=fail)      │
           │      └──────────────────────────────────────┘

           │ calibration_test_id

    ┌──────▼─────────────────────────────┐
    │  ToolCalibrationExternal           │
    │                                    │
    │  id                                │
    │  calibration_test_id               │
    │  partner_id (lab)                  │
    │  date_external_calibration         │
    │  description                       │
    └────────────────┬───────────────────┘

                     │ tool_calibration_external_id

    ┌────────────────▼──────────────────┐
    │  ToolCalibrationExternalFiles     │
    │                                   │
    │  id                               │
    │  tool_calibration_external_id     │
    │  file_id                          │
    └───────────────────────────────────┘

Key Entities

Tool

The primary entity representing physical tools requiring calibration.

Critical Fields:

  • calibration_cycle: Number (1, 3, 6, 12 months)
  • calibration_multipiler: Multiplier applied to cycle (default 1)
  • min_pull_force_n: Minimum pull force for this specific tool (optional override)
  • max_pull_force_n: Maximum pull force for this specific tool (optional override)
  • date_last_calibration: Date of last successful calibration
  • date_calibration_due: Calculated due date for next calibration
  • last_calibration_id: Foreign key to most recent ToolCalibration

Business Rules:

  • Must have calibration_cycle and calibration_multipiler set before calibration
  • Due date calculated: last_calibration_date + (cycle × multiplier)
  • Tool-specific force ranges override tool type defaults

ToolType

Defines categories of tools with shared calibration requirements.

Critical Fields:

  • calibration_cycle: Default cycle for tools of this type
  • calibration_multipiler: Default multiplier for tools of this type

Relationships:

  • Has many ToolCalibrationRequirement (defines required tests)
  • Has many ToolTypePartPullTest (defines wire sizes and force requirements)

ToolCalibrationRequirement

Links tool types to required calibration tests.

Purpose: Defines which calibration tests are mandatory for a given tool type.

Critical Fields:

  • tool_type_id: The tool type this requirement applies to
  • calibration_type_id: The type of calibration test required
  • calibration_standard_id: The standard to apply (e.g., IEC 60512-9-2)

Example:

sql
-- Crimping Tool requires pull test and visual inspection
INSERT INTO tool_calibration_requirement
  (tool_type_id, calibration_type_id, calibration_standard_id)
VALUES
  (5, 1, 1),  -- Pull test per IEC 60512-9-2
  (5, 2, NULL);  -- Visual inspection (no standard)

ToolCalibrationType

Defines types of calibration tests.

Controller Field: The controller field is critical - it determines how the system processes the test data.

Supported Controllers:

  • pull_test_dellner: Complex pull test with multiple wire sizes
  • visual: Simple pass/fail visual inspection
  • go_no_go: Gauge measurement test
  • pressure: Pressure calibration
  • temperature_test: Temperature calibration
  • external: External lab calibration

Extension Point: To add a new calibration type, create a new controller value and implement handling in ToolRepository.processCalibration().

ToolCalibration

Main calibration record representing a complete calibration event.

Critical Fields:

  • date_calibration: When calibration was performed
  • status: 1 (pass) or 0 (fail) - derived from ALL tests
  • user_id: Technician who performed calibration
  • tool_id: Tool being calibrated
  • calibration_cycle: Snapshot of cycle at calibration time
  • calibration_multipiler: Snapshot of multiplier at calibration time
  • date_last_calibration: Previous calibration date
  • date_calibration_due: Calculated next due date

Status Calculation:

typescript
// All tests must pass for overall pass
const overallStatus = testStatusesData.every(t => t.testStatus);

Date Handling:

  • For internal calibrations: Uses current timestamp
  • For mixed internal/external: Uses minimum external calibration date
  • Enables accurate due date calculation for externally calibrated tools

ToolCalibrationTest

Individual test within a calibration.

Critical Fields:

  • calibration_id: Parent calibration
  • calibration_type_id: Type of test
  • status: true (pass) or false (fail)
  • is_external: Whether performed by external lab
  • notes: Technician observations

Relationships:

  • Has many ToolCalibrationTestPullSimple (for pull tests)
  • Has many ToolCalibrationExternal (for external calibrations)
  • Has many ToolCalibrationTestTemperature (for temperature tests)

ToolCalibrationTestPullSimple

Detailed pull test measurements for a single wire size.

Critical Fields:

  • calibration_test_id: Parent test
  • tensile_tool_id: Tool used to perform pull test (tensile tester)
  • part_id: Specific part/component tested
  • tool_calibration_standard_id: Standard applied
  • wire_awg_value: Wire gauge (e.g., "20", "22", "24")
  • wire_mm_value: Wire diameter in millimeters
  • min_pull_force_n: Minimum required force
  • pull_test_value_n: Measured force in Newtons
  • status: true if pull_test_value_n >= min_pull_force_n

Pass Logic:

typescript
const testPassed = pull_test_value_n >= min_pull_force_n;

Business Logic

processCalibration Method

Located in: /workspace/api/b3api/src/repositories/tool.repository.ts

This is the core calibration processing engine.

Method Signature

typescript
async processCalibration(
  toolId: number,
  userId: number,
  calibrationDataMap: {
    [requirementId: string]: {
      testData: any;
      calibration_type: { controller: string };
    };
  }
): Promise<any>

Processing Flow

Phase 1: Validation

typescript
// 1. Validate tool exists
const tool = await this.findById(toolId);
if (!tool) throw new HttpErrors.NotFound('Tool not found');

// 2. Validate calibration configuration
if (!tool.calibration_cycle) {
  throw new HttpErrors.BadRequest(
    'Tool must have calibration_cycle set before calibration'
  );
}

if (!tool.calibration_multipiler || tool.calibration_multipiler === 0) {
  throw new HttpErrors.BadRequest(
    'Tool must have calibration_multipiler set before calibration'
  );
}

// 3. Validate all requirements are provided
const requirements = await toolCalibrationRequirementRepo.find({
  where: { tool_type_id: tool.tool_type_id }
});

const requirementIds = requirements.map(r => r.id?.toString());
const providedRequirementIds = Object.keys(calibrationDataMap);

for (const reqId of requirementIds) {
  if (!providedRequirementIds.includes(reqId!)) {
    throw new HttpErrors.BadRequest(
      `Missing calibration data for requirement ID: ${reqId}`
    );
  }
}

Phase 2: Status Calculation (Pre-Creation)

This critical phase calculates ALL test statuses BEFORE creating any database records. This ensures atomicity and correct overall status calculation.

typescript
const testStatusesData: Array<{
  requirement: any;
  testStatus: boolean;
  testData: any;
  calibration_type: any;
  pullTestDetails?: any;
}> = [];

for (const requirement of requirements) {
  const { testData, calibration_type } = calibrationDataMap[requirement.id];
  let testStatus = false;
  let pullTestDetails = null;

  // Calculate status based on calibration type
  if (testData.externalCalibration) {
    // External calibrations use testResult field
    testStatus = testData.testResult === 'pass';
  } else if (calibration_type.controller === 'pull_test_dellner') {
    // Internal pull test - complex calculation
    const pullTestResult = await this.calculatePullTestStatus(
      testData,
      toolTypePartPullTestRepo,
      toolStandardDiameterRepo,
      wireSizeRepo
    );
    testStatus = pullTestResult.overallStatus;
    pullTestDetails = pullTestResult.testDetails;
  } else if (calibration_type.controller === 'visual') {
    // Visual inspection
    testStatus = testData.testResult === 'pass';
  } else {
    // Other types use testResult
    testStatus = testData.testResult === 'pass';
  }

  testStatusesData.push({
    requirement,
    testStatus,
    testData,
    calibration_type,
    pullTestDetails
  });
}

// Calculate overall status - ALL tests must pass
const overallStatus = testStatusesData.every(t => t.testStatus);

Phase 3: Date Calculation

For external calibrations, use the minimum external calibration date to ensure accurate due date calculation:

typescript
let calibrationDate: string;

// Collect all external calibration dates
const externalDates: string[] = [];
for (const testStatusData of testStatusesData) {
  if (
    testStatusData.testData.externalCalibration &&
    testStatusData.testData.externalData?.calibrationDate
  ) {
    externalDates.push(testStatusData.testData.externalData.calibrationDate);
  }
}

// Use minimum external date or current date
if (externalDates.length > 0) {
  const momentDates = externalDates.map(date => moment(date, 'YYYY-MM-DD'));
  const minDate = moment.min(momentDates);
  calibrationDate = minDate.utc().toDate().toISOString();
} else {
  calibrationDate = moment().utc().toDate().toISOString();
}

Phase 4: Record Creation

Now that all statuses are calculated, create database records:

typescript
// Create main calibration record with correct status
const toolCalibration = await toolCalibrationRepo.create({
  date_calibration: calibrationDate,
  tool_id: toolId,
  user_id: userId,
  status: overallStatus ? 1 : 0,  // Correct overall status
  description: null,
  calibration_cycle: tool.calibration_cycle,
  calibration_multipiler: tool.calibration_multipiler,
  date_last_calibration: tool.date_last_calibration
});

// Create all test records
for (const testStatusData of testStatusesData) {
  const calibrationTest = await toolCalibrationTestRepo.create({
    calibration_id: toolCalibration.id,
    calibration_type_id: requirement.calibration_type_id,
    status: testStatus,  // Individual test status
    is_external: testData.externalCalibration || false,
    notes: testData.notes || null
  });

  // Create detailed records based on type
  if (calibration_type.controller === 'pull_test_dellner' && pullTestDetails) {
    await this.createPullTestRecords(
      pullTestDetails,
      calibrationTest.id,
      toolCalibrationTestPullSimpleRepo
    );
  }

  // Handle external calibration files
  if (testData.externalCalibration) {
    const externalRecord = await toolExternalCalibrationRepo.create({
      calibration_test_id: calibrationTest.id,
      date_external_calibration: moment(
        testData.externalData.calibrationDate,
        'YYYY-MM-DD'
      ).toDate().toISOString(),
      partner_id: testData.externalData.partnerId,
      description: testData.externalData.notes || null
    });

    // Link files to external calibration
    if (testData.externalData.uploadedFiles) {
      for (const file of testData.externalData.uploadedFiles) {
        await fileRepo.updateById(file.id, {
          status: 1,  // Active
          description: `$trl.external_calibration ${calibrationTest.id}`
        });

        await toolCalibrationExternalFilesRepo.create({
          tool_calibration_external_id: externalRecord.id,
          file_id: file.id
        });
      }
    }
  }
}

calculatePullTestStatus Method

Handles the complex pull test status calculation.

typescript
private async calculatePullTestStatus(
  testData: any,
  toolTypePartPullTestRepo: ToolTypePartPullTestRepository,
  toolStandardDiameterRepo: ToolStandardDiameterRepository,
  wireSizeRepo: WireSizeRepository
): Promise<{ overallStatus: boolean; testDetails: any[] }>

Algorithm:

  1. For each pull test result in testData.pullTestResults:

    • Retrieve ToolTypePartPullTest record
    • Get associated ToolStandardDiameter for minimum force
    • Get WireSize for wire specifications
    • Parse measured force from result string
    • Compare: measured >= minimum
    • Collect individual test status
  2. Overall pull test status:

    • ALL individual wire size tests must pass
    • Return overall status and detailed test data
typescript
const pullTestResults = testData.pullTestResults || [];
const allTestStatuses: boolean[] = [];
const testDetails: any[] = [];

for (const pullTestResult of pullTestResults) {
  const pull_test_value_n = parseFloat(pullTestResult.result) || 0;
  const min_pull_force_n = toolStandardDiameter.min_pull_force_n;

  // Individual test pass/fail
  const testPassed = pull_test_value_n >= min_pull_force_n;
  allTestStatuses.push(testPassed);

  testDetails.push({
    toolTypePartPullTestId: pullTestResult.id,
    part_id: toolTypePartPullTest.tool_part_id,
    standard_id: toolStandardDiameter.standard_id,
    wire_awg_value: wireSize.awg_value,
    wire_mm_value: wireSize.diameter_mm,
    min_pull_force_n,
    pull_test_value_n,
    tensile_tool_id: testData.tensile_tool_id || null,
    status: testPassed
  });
}

// ALL must pass
const overallStatus = allTestStatuses.every(status => status);
return { overallStatus, testDetails };

Database Schema

Key Tables

tool

sql
CREATE TABLE tool (
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(255) NOT NULL,
  tool_type_id INT,
  calibration_cycle INT,
  calibration_multipiler DECIMAL(10,4),
  min_pull_force_n DECIMAL(8,2),
  max_pull_force_n DECIMAL(8,2),
  date_last_calibration DATETIME,
  date_calibration_due DATETIME,
  last_calibration_id INT,
  system_status_id INT,
  calibration_required BOOLEAN DEFAULT FALSE,
  -- ... other fields
  FOREIGN KEY (tool_type_id) REFERENCES tool_type(id),
  FOREIGN KEY (last_calibration_id) REFERENCES tool_calibration(id)
);

tool_calibration

sql
CREATE TABLE tool_calibration (
  id INT PRIMARY KEY AUTO_INCREMENT,
  tool_id INT NOT NULL,
  user_id INT NOT NULL,
  date_calibration DATETIME DEFAULT NOW(),
  date_last_calibration DATETIME,
  date_calibration_due DATETIME,
  status INT,  -- 1 = pass, 0 = fail
  calibration_cycle INT,
  calibration_multipiler DECIMAL(10,4),
  description LONGTEXT,
  FOREIGN KEY (tool_id) REFERENCES tool(id),
  FOREIGN KEY (user_id) REFERENCES user(id)
);

tool_calibration_test

sql
CREATE TABLE tool_calibration_test (
  id INT PRIMARY KEY AUTO_INCREMENT,
  calibration_id INT NOT NULL,
  calibration_type_id INT NOT NULL,
  status BOOLEAN,  -- true = pass, false = fail
  is_external BOOLEAN DEFAULT FALSE,
  notes LONGTEXT,
  FOREIGN KEY (calibration_id) REFERENCES tool_calibration(id),
  FOREIGN KEY (calibration_type_id) REFERENCES tool_calibration_type(id)
);

tool_calibration_test_pull_simple

sql
CREATE TABLE tool_calibration_test_pull_simple (
  id INT PRIMARY KEY AUTO_INCREMENT,
  calibration_test_id INT NOT NULL,
  tensile_tool_id INT NOT NULL,
  part_id INT NOT NULL,
  tool_calibration_standard_id INT NOT NULL,
  wire_awg_value VARCHAR(50),
  wire_mm_value VARCHAR(50),
  min_pull_force_n DECIMAL(8,2) NOT NULL,
  pull_test_value_n DECIMAL(8,2) NOT NULL,
  status BOOLEAN,  -- true = pass, false = fail
  FOREIGN KEY (calibration_test_id) REFERENCES tool_calibration_test(id),
  FOREIGN KEY (tensile_tool_id) REFERENCES tool(id),
  FOREIGN KEY (part_id) REFERENCES part(id),
  FOREIGN KEY (tool_calibration_standard_id) REFERENCES tool_calibration_standard(id)
);

Indexes

Critical indexes for performance:

sql
-- Find tools due for calibration
CREATE INDEX idx_tool_calibration_due
  ON tool(date_calibration_due, system_status_id);

-- Find calibrations by tool
CREATE INDEX idx_calibration_tool_id
  ON tool_calibration(tool_id, date_calibration DESC);

-- Find tests by calibration
CREATE INDEX idx_calibration_test_calibration_id
  ON tool_calibration_test(calibration_id);

-- Find pull test details
CREATE INDEX idx_pull_simple_test_id
  ON tool_calibration_test_pull_simple(calibration_test_id);

Validation Logic

Request Validation

The system enforces strict validation at multiple levels:

1. Tool Configuration Validation

typescript
if (!tool.calibration_cycle || tool.calibration_cycle === null) {
  throw new HttpErrors.BadRequest(
    'Tool must have calibration_cycle set before calibration'
  );
}

if (!tool.calibration_multipiler || tool.calibration_multipiler === 0) {
  throw new HttpErrors.BadRequest(
    'Tool must have calibration_multipiler set before calibration'
  );
}

2. Completeness Validation

ALL requirements must be provided:

typescript
const requirements = await toolCalibrationRequirementRepo.find({
  where: { tool_type_id: tool.tool_type_id }
});

for (const reqId of requirementIds) {
  if (!providedRequirementIds.includes(reqId!)) {
    throw new HttpErrors.BadRequest(
      `Missing calibration data for requirement ID: ${reqId}`
    );
  }
}

3. Data Format Validation

  • External calibration dates must be in YYYY-MM-DD format
  • Pull test results must be parseable as numbers
  • Test result values must be "pass" or "fail"
  • File IDs must reference existing uploaded files

Status Calculation Algorithm

Overall Calibration Status

Overall Status = AND(all test statuses)

If ALL tests pass → Overall Status = 1 (pass)
If ANY test fails → Overall Status = 0 (fail)

Pull Test Status

Pull Test Status = AND(all wire size test statuses)

For each wire size:
  Wire Test Status = (measured_force >= min_required_force)

Pull Test Passes = ALL wire tests pass

Visual/Go-No-Go Test Status

Test Status = (testResult === "pass")

External Calibration Status

Test Status = (testResult === "pass")

The system trusts the external lab's determination.

Extension Points

Adding New Calibration Types

Step 1: Database Setup

Add new ToolCalibrationType record:

sql
INSERT INTO tool_calibration_type (name, description, controller)
VALUES ('Torque Test', 'Torque wrench calibration', 'torque_test');

Step 2: Implement Controller Logic

In ToolRepository.processCalibration(), add handling:

typescript
// Around line 545
if (calibration_type.controller === 'torque_test') {
  // Calculate torque test status
  const torqueTestResult = await this.calculateTorqueTestStatus(
    testData,
    /* additional repos */
  );
  testStatus = torqueTestResult.overallStatus;
  torqueTestDetails = torqueTestResult.testDetails;
}

Step 3: Implement Calculation Method

typescript
private async calculateTorqueTestStatus(
  testData: any,
  /* repositories */
): Promise<{ overallStatus: boolean; testDetails: any[] }> {
  // Your calculation logic
  const torqueValue = parseFloat(testData.torqueValue);
  const minTorque = testData.minTorque;
  const maxTorque = testData.maxTorque;

  const testPassed = torqueValue >= minTorque && torqueValue <= maxTorque;

  return {
    overallStatus: testPassed,
    testDetails: [{
      torqueValue,
      minTorque,
      maxTorque,
      status: testPassed
    }]
  };
}

Step 4: Create Detail Table (if needed)

sql
CREATE TABLE tool_calibration_test_torque (
  id INT PRIMARY KEY AUTO_INCREMENT,
  calibration_test_id INT NOT NULL,
  min_torque_nm DECIMAL(8,2) NOT NULL,
  max_torque_nm DECIMAL(8,2) NOT NULL,
  measured_torque_nm DECIMAL(8,2) NOT NULL,
  status BOOLEAN,
  FOREIGN KEY (calibration_test_id) REFERENCES tool_calibration_test(id)
);

Step 5: Implement Record Creation

In processCalibration() Phase 4:

typescript
if (calibration_type.controller === 'torque_test' && torqueTestDetails) {
  await this.createTorqueTestRecords(
    torqueTestDetails,
    calibrationTest.id,
    toolCalibrationTestTorqueRepo
  );
}

Custom Validation Rules

To add custom validation for specific tool types:

typescript
// In processCalibration() after tool retrieval
if (tool.tool_type_id === SPECIAL_TOOL_TYPE_ID) {
  // Custom validation
  if (!testData.specialField) {
    throw new HttpErrors.BadRequest('Special field required for this tool type');
  }
}

Custom Status Calculation

To implement custom status logic (e.g., weighted scoring):

typescript
// Replace simple every() check with custom logic
const overallStatus = this.calculateCustomStatus(testStatusesData, tool);

private calculateCustomStatus(
  testStatusesData: any[],
  tool: Tool
): boolean {
  // Example: Critical tests must pass, others are weighted
  const criticalTests = testStatusesData.filter(t => t.isCritical);
  const nonCriticalTests = testStatusesData.filter(t => !t.isCritical);

  const criticalPass = criticalTests.every(t => t.testStatus);
  const nonCriticalScore = nonCriticalTests.filter(t => t.testStatus).length;
  const nonCriticalThreshold = nonCriticalTests.length * 0.8; // 80% must pass

  return criticalPass && (nonCriticalScore >= nonCriticalThreshold);
}

Performance Considerations

Query Optimization

Loading Calibrations with Full Details:

typescript
// Efficient single query with deep includes
const filter = {
  where: { tool_id: toolId },
  include: [
    {
      relation: 'tests',
      scope: {
        include: [
          'calibration_type',
          {
            relation: 'pull_tests_simple',
            scope: {
              include: ['standard', 'tensile_tool', 'part']
            }
          },
          {
            relation: 'external',
            scope: {
              include: ['partner']
            }
          }
        ]
      }
    },
    'user',
    'tool',
    'calibration_cycle_format'
  ]
};

Transaction Handling

The processCalibration method performs multiple database writes. Consider wrapping in a transaction for atomicity:

typescript
// Pseudo-code - LoopBack 4 transaction pattern
async processCalibration(...) {
  const transaction = await this.dataSource.beginTransaction();

  try {
    // All database operations
    const toolCalibration = await toolCalibrationRepo.create(
      { ... },
      { transaction }
    );

    // ... more operations

    await transaction.commit();
    return result;
  } catch (error) {
    await transaction.rollback();
    throw error;
  }
}

Caching Strategies

For frequently accessed reference data:

typescript
// Cache calibration types
private calibrationTypeCache: Map<number, ToolCalibrationType> = new Map();

async getCalibrationType(id: number): Promise<ToolCalibrationType> {
  if (!this.calibrationTypeCache.has(id)) {
    const type = await this.calibrationTypeRepo.findById(id);
    this.calibrationTypeCache.set(id, type);
  }
  return this.calibrationTypeCache.get(id)!;
}

Error Handling

Common Error Scenarios

Tool Not Found

typescript
if (!tool) {
  throw new HttpErrors.NotFound(`Tool with ID ${toolId} not found`);
}

Missing Configuration

typescript
if (!tool.calibration_cycle) {
  throw new HttpErrors.BadRequest(
    'Tool must have calibration_cycle set before calibration'
  );
}

Incomplete Data

typescript
if (!providedRequirementIds.includes(reqId)) {
  throw new HttpErrors.BadRequest(
    `Missing calibration data for requirement ID: ${reqId}`
  );
}

Invalid Data Format

typescript
const pull_test_value_n = parseFloat(pullTestResult.result);
if (isNaN(pull_test_value_n)) {
  throw new HttpErrors.BadRequest(
    `Invalid pull test value: ${pullTestResult.result}`
  );
}

Error Recovery

For partial failures during record creation, consider implementing compensating transactions or maintaining operation logs for audit trails.

Security Considerations

Authorization

Ensure proper authorization checks:

typescript
// In controller before calling processCalibration
if (!this.userHasPermission(userId, 'tool.calibrate')) {
  throw new HttpErrors.Forbidden('User not authorized to calibrate tools');
}

Data Integrity

  • All calibration records are immutable (no updates, only creates)
  • Maintains full audit trail through user_id and timestamps
  • File associations prevent orphaned calibration certificates

Input Sanitization

  • Validate all numeric inputs are within reasonable ranges
  • Sanitize text fields (notes, descriptions) to prevent injection
  • Verify file uploads are legitimate calibration certificates

Monitoring and Observability

Key Metrics to Track

  • Calibration Success Rate: Percentage of calibrations passing
  • Time to Calibrate: Average duration per calibration type
  • Tool Downtime: Time tools spend in calibration vs. production
  • Overdue Tools: Count of tools past due date
  • Failure Patterns: Tools or tool types with recurring failures

Logging Recommendations

typescript
// Log calibration events
logger.info('Calibration started', {
  toolId,
  userId,
  requirementCount: requirements.length
});

logger.info('Calibration completed', {
  toolId,
  calibrationId: toolCalibration.id,
  overallStatus,
  testCount: testStatusesData.length,
  passedTests: testStatusesData.filter(t => t.testStatus).length
});

// Log failures with details
if (!overallStatus) {
  logger.warn('Calibration failed', {
    toolId,
    calibrationId: toolCalibration.id,
    failedTests: testStatusesData
      .filter(t => !t.testStatus)
      .map(t => t.calibration_type.name)
  });
}

Testing Strategy

Unit Tests

Test individual calculation methods:

typescript
describe('calculatePullTestStatus', () => {
  it('should pass when all measurements exceed minimums', async () => {
    const testData = {
      pullTestResults: [
        { id: 1, result: '50.0' },  // min: 45
        { id: 2, result: '40.0' },  // min: 38
        { id: 3, result: '32.0' }   // min: 30
      ]
    };

    const result = await repo.calculatePullTestStatus(testData, ...);

    expect(result.overallStatus).toBe(true);
    expect(result.testDetails).toHaveLength(3);
    expect(result.testDetails.every(t => t.status)).toBe(true);
  });

  it('should fail when any measurement is below minimum', async () => {
    const testData = {
      pullTestResults: [
        { id: 1, result: '50.0' },  // min: 45 - pass
        { id: 2, result: '35.0' },  // min: 38 - FAIL
        { id: 3, result: '32.0' }   // min: 30 - pass
      ]
    };

    const result = await repo.calculatePullTestStatus(testData, ...);

    expect(result.overallStatus).toBe(false);
    expect(result.testDetails[1].status).toBe(false);
  });
});

Integration Tests

Test full calibration workflow:

typescript
describe('processCalibration', () => {
  it('should create complete calibration record', async () => {
    const result = await toolRepo.processCalibration(
      toolId,
      userId,
      calibrationDataMap
    );

    expect(result.success).toBe(true);
    expect(result.calibrationId).toBeDefined();

    // Verify database records
    const calibration = await calibrationRepo.findById(result.calibrationId, {
      include: ['tests']
    });

    expect(calibration.status).toBe(1);
    expect(calibration.tests).toHaveLength(2);
  });
});

Migration Guide

Knex Migrations

Key migrations for calibration system:

Initial calibration tables:

bash
/workspace/api/b3api/src/knex_migrations/20240527203454_add_is_external_to_tool_calibration_test.ts

Pull test simplification:

bash
/workspace/api/b3api/src/knex_migrations/20251019094232_add_new_table_tool_calibration_test_pull_simple.ts

Calibration controller endpoint:

bash
/workspace/api/b3api/src/knex_migrations/20251019085223_add_new_controller_tool_calibration.ts

Data Migration Considerations

When migrating from legacy calibration system:

  1. Map Old Test Types: Create mapping from old test type IDs to new ToolCalibrationType records
  2. Preserve History: Migrate historical calibration records maintaining relationships
  3. Recalculate Due Dates: Run script to recalculate date_calibration_due for all tools
  4. Validate Integrity: Ensure all foreign keys are properly linked

Syneo/Barcoding Documentation