Planned Maintenance
Counter-driven maintenance, ranked.
The PMS produces something deceptively simple: a list of jobs with due dates. The work is interpreting that list. A vessel with 200 overdue routine jobs is in better shape than one with 5 overdue critical-machinery jobs. The pipeline scores all of that and surfaces the one number a TSI actually needs: where this vessel ranks against the rest of the fleet.
Two job types, one threshold logic
| Job type | Trigger | Due-date driver |
|---|---|---|
| Counter-based | Running hours, generator hours, compressor cycles | H_job − H_current |
| Calendar-based | Wall-clock interval (monthly, quarterly, annual) | D_due − D_today |
The pipeline handles both with the same threshold logic but reports them separately — a vessel idle in port may not be falling behind on counter-based jobs even if the calendar says otherwise.
Counter-based overdue (hours): H_current − H_job_due
Calendar-based status (days): Δ_days ≤ 0 → Overdue 0 < Δ_days ≤ 30 → Due Soon Δ_days > 30 → In OrderThe four major-machinery rollups
The PMS surveillance produces four major-machinery rollups — each computed identically but on a different equipment family:
Main Engine — the most expensive equipment to defer. Delayed cylinder overhauls show up as cylinder asymmetry on the ME performance review before they show up as engine failure.
Auxiliary Engines — each AE reported separately. When one is in maintenance, the redundancy tier of the rest matters — cross-reference AE performance for blackout-risk implications.
Purifiers — fuel and lube-oil purifiers accumulate counter hours fast. A neglected purifier is the most common upstream cause of cat-fine ingress and lube-oil contamination.
Compressors — air compressors drive emergency starts, pneumatic systems, and air-driven safety equipment. PMS lapses tend to be invisible until something fails to start.
Each rollup produces the same output shape:
| Code | Title | Equipment | Due Hours | Current Hours | Overdue Hours |
|---|---|---|---|---|---|
| ME-CYL-OH | Cylinder overhaul | ME Cyl 5 | 24,000 | 24,820 | 820 |
| ME-FUEL-INJ | Fuel injector replacement | ME Cyl 3 | 8,000 | 8,180 | 180 |
| ME-TC-WASH | Turbocharger water-wash | ME T/C | 1,500 | 1,420 | — |
The maintenance-debt index
Every vessel gets a single composite score that ranks it against the fleet:
Debt index = 5 × N_critical + 2 × N_major + 1 × N_routine + 3 × N_chronicN_chronic = jobs overdue for 3+ consecutive periods, with a +3 multiplier.
| Index delta | What it means |
|---|---|
| Falling | Closure rate exceeds arrival rate — vessel is recovering |
| Stable | Steady state |
| Rising | Drift — the gap compounds over time |
Two weeks of “rising” is enough to escalate even if the absolute index is below the fleet median. Direction matters as much as magnitude.
Fleet snapshot — weekly run
A weekly run across a 14-vessel fleet:
| Vessel | Compliance % | Debt index | Trend | Critical open | Verdict |
|---|---|---|---|---|---|
| POSUN | 96.4% | 31 | ↘ | 1 | OK |
| AQUILA | 91.2% | 67 | → | 3 | Monitor |
| OCEAN | 88.7% | 124 | ↗ | 6 | Escalate |
| NEXUS | 94.1% | 22 | ↘ | 0 | OK |
OCEAN is the first action: falling compliance, rising debt index, six critical jobs open. AQUILA is the borderline case for next-week revisit. The pipeline routes OCEAN to TSI automatically.
Under the hood
Compliance percentage — the headline metric
Compliance % = N_jobs_completed_on_schedule / N_mandatory_jobs_in_period × 100Below 90% is flagged. Below 80% triggers escalation regardless of the criticality breakdown — at that point the maintenance system itself is the issue, not individual jobs.
Job-code links — implementation
The major-machinery rollup templates hyperlink each job code to the ERP record:
def add_jobcode_links(df): df["jobCode"] = df.apply( lambda x: ( f"<a href=\"{x['link']}\" target=\"_blank\">{x['jobCode']}</a>" if pd.notna(x["link"]) and x["link"] != "" else x["jobCode"] ), axis=1, ) return df
def filter_and_rename_columns(df): df = df[["jobCode", "jobTitle", "component", "jobDueCounter", "currentCounter", "overdue", "imo"]].rename( columns={ "jobCode": "Code", "jobTitle": "Title", "component": "Equipment", "jobDueCounter": "Due Hours", "currentCounter": "Current Hours", "overdue": "Overdue Hours", } ) return dfPMS Summary — five compliance views in one document
Beyond the machinery rollups, the PMS Summary template assembles five compliance views:
- Maintenance jobs — open and overdue jobs by criticality and machinery family.
- Certificates — expired or about-to-expire certificates, cross-referenced with the class pipeline so a class-certificate gap and a PMS gap appear in the same brief.
- Critical spares — spares marked critical with current quantity below minimum level. A critical-spares stockout is a future maintenance overrun the vessel doesn’t know about yet.
- Machinery defects — open defects on main and auxiliary engine families, cross-referenced with the defects pipeline for severity and age.
- Counter updation — whether the vessel has been updating engine counters at the expected cadence. Stale counters make the entire counter-based PMS view unreliable — itself a compliance issue.
A reviewer reading the Summary sees all five at once. The Summary is graded on the worst of the five, not the average.
Escalation triggers
| Trigger | Severity |
|---|---|
| Any CRITICAL job overdue | CRITICAL |
| Compliance below 80% | HIGH |
| Chronic overrun (3+ periods) on any safety-critical job | CRITICAL |
| Equipment cluster (3+ jobs on same major-machinery family) | HIGH |
| Class item overdue past deadline | CRITICAL |
| Counter updation lapse (stale counters > 14 days) | HIGH |
Data sources
| Source | What it provides |
|---|---|
| Vessel ERP (PMS module) | Job register, due dates, completion records, criticality tags |
| Engine running-hours feed | Counter readings — drives counter-based job due dates |
| PMS counter forms (vessel-side) | Manual counter updates when telemetry is unavailable |
| Critical spares inventory (ERP) | Stock vs minimum levels |
PMS data lives entirely in the ERP. The pipeline reads job records and reconciles counter freshness against engine running-hours from the noon-report stream — discrepancies surface as counter-updation gaps.