Skip to content

MonzerDev/fabmetrics-telemetry

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 

Repository files navigation

FabMetrics Telemetry System

Project Overview

This is a local backend system that helps factories track machine production. Machines send periodic telemetry events containing production counts and operational status. This API persists these events in a SQLite database and exposes simple REST endpoints for querying total production metrics and machine efficiency ratios.

How to Run

  1. Install dependencies:

    pip install -r requirements.txt
  2. Start the server:

    uvicorn app.main:app --reload
  3. Access API documentation: Open your browser to http://127.0.0.1:8000/docs

The database file (fabmetrics.db) is created automatically on first startup.

Project Structure

monzer_fabmetrics_task/
├── app/
│   ├── __init__.py      # Package marker
│   ├── main.py          # FastAPI application entry point with lifespan management
│   ├── database.py      # SQLite connection and query functions
│   ├── models.py        # Pydantic request/response schemas
│   └── routes.py        # API endpoint definitions
├── fabmetrics.db        # SQLite database (auto-created on first run)
├── requirements.txt     # Python dependencies
└── README.md

Database Schema

CREATE TABLE IF NOT EXISTS production_events (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    machine_id TEXT NOT NULL,
    timestamp TEXT NOT NULL,
    produced_item_count INTEGER NOT NULL,
    machine_status TEXT NOT NULL CHECK (machine_status IN ('running', 'stopped')),
    created_at TEXT DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX IF NOT EXISTS idx_machine_id ON production_events(machine_id);
CREATE INDEX IF NOT EXISTS idx_timestamp ON production_events(timestamp);

Column Descriptions

Column Type Description
id INTEGER Auto-incrementing primary key
machine_id TEXT Identifier for the machine (e.g., "M1", "M2")
timestamp TEXT Event time in ISO 8601 format (stored as-is from client)
produced_item_count INTEGER Number of items produced in this event
machine_status TEXT Machine state: 'running' or 'stopped'
created_at TEXT Server timestamp when record was inserted

API Endpoints

1. POST /events

Create a new production event.

Request Body:

{
  "machine_id": "M1",
  "timestamp": "2024-01-15T10:30:00",
  "produced_item_count": 100,
  "machine_status": "running"
}

Example Request:

curl -X POST "http://127.0.0.1:8000/events" \
  -H "Content-Type: application/json" \
  -d '{"machine_id": "M1", "timestamp": "2024-01-15T10:30:00", "produced_item_count": 100, "machine_status": "running"}'

Example Response (201 Created):

{
  "id": 1,
  "machine_id": "M1",
  "timestamp": "2024-01-15T10:30:00",
  "produced_item_count": 100,
  "machine_status": "running"
}

2. GET /production/total

Get total production count for all machines or a specific machine.

Query Parameters:

  • machine_id (optional): Filter to a specific machine

Behavior:

  • Without machine_id: Returns a list of totals for all machines
  • With machine_id: Returns a single object for that machine (404 if not found)

Example Request (all machines):

curl "http://127.0.0.1:8000/production/total"

Example Response (list):

[
  {"machine_id": "M1", "total_produced": 250},
  {"machine_id": "M2", "total_produced": 240}
]

Example Request (specific machine):

curl "http://127.0.0.1:8000/production/total?machine_id=M1"

Example Response (single object):

{"machine_id": "M1", "total_produced": 250}

Example Response (machine not found - 404):

{"detail": "Machine 'UNKNOWN' not found"}

3. GET /production/range

Get total production per machine within a time range.

Query Parameters:

  • start (required): Start timestamp in ISO 8601 format
  • end (required): End timestamp in ISO 8601 format
  • machine_id (optional): Filter to a specific machine

Example Request:

curl "http://127.0.0.1:8000/production/range?start=2024-01-15T00:00:00&end=2024-01-15T23:59:59"

Example Response:

[
  {"machine_id": "M1", "total_produced": 250},
  {"machine_id": "M2", "total_produced": 240}
]

Example Request (with machine filter):

curl "http://127.0.0.1:8000/production/range?start=2024-01-15T00:00:00&end=2024-01-15T23:59:59&machine_id=M1"

Example Response:

[
  {"machine_id": "M1", "total_produced": 250}
]

4. GET /efficiency/{machine_id}

Get efficiency metrics for a specific machine.

Path Parameters:

  • machine_id (required): The machine identifier

Example Request:

curl "http://127.0.0.1:8000/efficiency/M1"

Example Response:

{
  "machine_id": "M1",
  "total_events": 3,
  "running_events": 2,
  "efficiency_ratio": 0.6666666666666666
}

Example Response (machine not found - 404):

{"detail": "Machine 'UNKNOWN' not found"}

Efficiency Definition

The efficiency ratio is calculated as:

efficiency_ratio = running_events / total_events

Where:

  • running_events = count of events where machine_status = 'running'
  • total_events = count of all events for that machine

This measures the proportion of recorded events where the machine was running. It does not infer continuous runtime or estimate duration between events.

Example: If a machine has 3 events (2 running, 1 stopped), its efficiency ratio is 2/3 ≈ 0.67.

Verification Results

All endpoints tested and verified:

Test Endpoint Expected Actual Status
1 GET /production/total M1: 250, M2: 240 M1: 250, M2: 240 ✅ PASS
2 GET /production/total?machine_id=M1 M1: 250 M1: 250 ✅ PASS
3 GET /efficiency/M1 3 events, 2 running, 0.667 3 events, 2 running, 0.667 ✅ PASS
4 GET /efficiency/M2 2 events, 2 running, 1.0 2 events, 2 running, 1.0 ✅ PASS

Test Scenarios

Below are manual test cases to verify the system works correctly.

Setup: Insert Test Events

Insert 3 events for machine "M1" (2 running, 1 stopped):

curl -X POST "http://127.0.0.1:8000/events" \
  -H "Content-Type: application/json" \
  -d '{"machine_id": "M1", "timestamp": "2024-01-15T10:00:00", "produced_item_count": 100, "machine_status": "running"}'

curl -X POST "http://127.0.0.1:8000/events" \
  -H "Content-Type: application/json" \
  -d '{"machine_id": "M1", "timestamp": "2024-01-15T11:00:00", "produced_item_count": 80, "machine_status": "running"}'

curl -X POST "http://127.0.0.1:8000/events" \
  -H "Content-Type: application/json" \
  -d '{"machine_id": "M1", "timestamp": "2024-01-15T12:00:00", "produced_item_count": 70, "machine_status": "stopped"}'

Insert 2 events for machine "M2" (both running):

curl -X POST "http://127.0.0.1:8000/events" \
  -H "Content-Type: application/json" \
  -d '{"machine_id": "M2", "timestamp": "2024-01-15T10:30:00", "produced_item_count": 90, "machine_status": "running"}'

curl -X POST "http://127.0.0.1:8000/events" \
  -H "Content-Type: application/json" \
  -d '{"machine_id": "M2", "timestamp": "2024-01-15T11:30:00", "produced_item_count": 150, "machine_status": "running"}'

Test 1: Query total production for all machines

curl "http://127.0.0.1:8000/production/total"

Expected Response:

[
  {"machine_id": "M1", "total_produced": 250},
  {"machine_id": "M2", "total_produced": 240}
]

Test 2: Query total production for "M1" only

curl "http://127.0.0.1:8000/production/total?machine_id=M1"

Expected Response:

{"machine_id": "M1", "total_produced": 250}

Test 3: Query production in a time range

curl "http://127.0.0.1:8000/production/range?start=2024-01-15T00:00:00&end=2024-01-15T23:59:59"

Expected Response:

[
  {"machine_id": "M1", "total_produced": 250},
  {"machine_id": "M2", "total_produced": 240}
]

Test 4: Query production in a time range for specific machine

curl "http://127.0.0.1:8000/production/range?start=2024-01-15T10:00:00&end=2024-01-15T11:30:00&machine_id=M1"

Expected Response:

[
  {"machine_id": "M1", "total_produced": 180}
]

(Only the first two M1 events fall within this range: 100 + 80 = 180)


Test 5: Query efficiency for "M1"

curl "http://127.0.0.1:8000/efficiency/M1"

Expected Response:

{
  "machine_id": "M1",
  "total_events": 3,
  "running_events": 2,
  "efficiency_ratio": 0.6666666666666666
}

Test 6: Query efficiency for "M2"

curl "http://127.0.0.1:8000/efficiency/M2"

Expected Response:

{
  "machine_id": "M2",
  "total_events": 2,
  "running_events": 2,
  "efficiency_ratio": 1.0
}

Test 7: Attempt to insert invalid event (missing field)

curl -X POST "http://127.0.0.1:8000/events" \
  -H "Content-Type: application/json" \
  -d '{"machine_id": "M1", "timestamp": "2024-01-15T10:00:00", "produced_item_count": 100}'

Expected Response (422):

{
  "detail": [
    {
      "type": "missing",
      "loc": ["body", "machine_status"],
      "msg": "Field required",
      "input": {...}
    }
  ]
}

Test 8: Query non-existent machine via /production/total

curl "http://127.0.0.1:8000/production/total?machine_id=UNKNOWN"

Expected Response (404):

{"detail": "Machine 'UNKNOWN' not found"}

Test 9: Query efficiency for non-existent machine

curl "http://127.0.0.1:8000/efficiency/UNKNOWN"

Expected Response (404):

{"detail": "Machine 'UNKNOWN' not found"}

Test 10: Invalid timestamp format

curl "http://127.0.0.1:8000/production/range?start=invalid-date&end=2024-01-15T23:59:59"

Expected Response (422):

{"detail": "Invalid timestamp format. Use ISO 8601."}

Test 11: Invalid machine_status

curl -X POST "http://127.0.0.1:8000/events" \
  -H "Content-Type: application/json" \
  -d '{"machine_id": "M1", "timestamp": "2024-01-15T10:00:00", "produced_item_count": 100, "machine_status": "idle"}'

Expected Response (422):

{
  "detail": [
    {
      "type": "value_error",
      "loc": ["body", "machine_status"],
      "msg": "Value error, machine_status must be 'running' or 'stopped'",
      "input": "idle"
    }
  ]
}

Assumptions and Limitations

  1. Timestamps stored as-is: Timestamps are stored exactly as provided by the client without timezone conversion. ISO 8601 format is validated but not normalized.

  2. Event-count based efficiency: The efficiency ratio measures the proportion of events where the machine was running. It does not infer continuous runtime or calculate duration between events.

  3. No authentication: This API is intended for local development and does not implement authentication or authorization.

  4. Single SQLite database: Uses a single SQLite file (fabmetrics.db) suitable for local development only. Not designed for production deployment or concurrent heavy loads.

  5. Return type varies for /production/total:

    • Without machine_id parameter: returns a list of all machines
    • With machine_id parameter: returns a single object for that machine (or 404)
  6. Empty results: List endpoints (/production/total without filter, /production/range) return an empty list [] when no data matches, not a 404 error.

  7. No additional endpoints: Only the four specified endpoints are implemented. No health checks, no bulk operations, no delete operations.

  8. Timestamp comparison: Time range queries compare timestamps as strings. This works correctly for ISO 8601 format since it sorts lexicographically in chronological order.

About

A production telemetry API for tracking factory machine output

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages