# 07 - RouteTrack Pi — Automated Route Processing (systemd Service + Timer)

#### **Date:** December 25, 2025  
**Category:** Raspberry Pi / GPS / Automation / systemd  
**Backlink:** 06 – [RouteTrack Pi — Route Processing &amp; Summary Generation](https://docs.natenetworks.com/books/06-raspberry-pi-python-linux-tips/page/06-routetrack-pi-route-processing-summary-generation)

---

## Project Goal

This phase automates RouteTrack’s daily processing workflow so I don’t have to manually run the processor.

Because the GPS logger continuously writes to SQLite, the route processor must run with **exclusive database access**. The automation is designed to safely:

1. Stop `routetrack-logger.service` (release DB lock)
2. Run `routetrack-process.py` using the venv Python
3. Restart `routetrack-logger.service`
4. Log all output to systemd journal for review

This creates a repeatable, production-style “ingest → process → report” pipeline.

---

## Why systemd Timer (instead of cron)

I used a systemd timer because it provides:

- Reliable scheduling with clear “next run” info
- Centralized logs via `journalctl`
- Easy enable/disable and status checks
- Clean separation of concerns (service runs once; timer schedules it)

---

## Create Processor Wrapper Script

This wrapper script is the “safe runner” that prevents SQLite lock errors.

### Create the file

```bash
sudo nano /opt/routetrack/bin/routetrack-run-processor.sh

```

### Script used

```bash
#!/usr/bin/env bash
set -euo pipefail

# ------------------------------------------------------------
# RouteTrack - Safe Processor Runner
# ------------------------------------------------------------
# Stops the GPS logger (releases SQLite locks),
# runs the daily processor, then starts the logger again.
# ------------------------------------------------------------

LOGGER_SERVICE="routetrack-logger.service"
PROCESSOR="/opt/routetrack/bin/routetrack-process.py"
PYTHON="/opt/routetrack/venv/bin/python"

echo "$(date -Is) RouteTrack processor wrapper starting..."

echo "$(date -Is) Stopping ${LOGGER_SERVICE}..."
systemctl stop "${LOGGER_SERVICE}"

# small pause so file handles release cleanly
sleep 2

echo "$(date -Is) Running route processor..."
"${PYTHON}" "${PROCESSOR}"

echo "$(date -Is) Starting ${LOGGER_SERVICE}..."
systemctl start "${LOGGER_SERVICE}"

echo "$(date -Is) RouteTrack processor wrapper completed."

```

Make executable:

```bash
sudo chmod +x /opt/routetrack/bin/routetrack-run-processor.sh

```

---

## Create the systemd Service (oneshot)

The systemd service runs the wrapper script one time.

### Create the unit file

```bash
sudo nano /etc/systemd/system/routetrack-processor.service

```

### Unit file used

```ini
[Unit]
Description=RouteTrack Daily Route Processor
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
User=root
Group=root
ExecStart=/opt/routetrack/bin/routetrack-run-processor.sh
StandardOutput=journal
StandardError=journal

```

Reload units:

```bash
sudo systemctl daemon-reload

```

---

## Test the Service Manually

Run it once to confirm it works:

```bash
sudo systemctl start routetrack-processor.service

```

View the logs:

```bash
journalctl -u routetrack-processor.service --no-pager -l

```

---

## Confirm Logger Restarted Properly

```bash
systemctl status routetrack-logger.service --no-pager -l

```

The logger returned to an **active (running)** state immediately after processing.

---

## Create the systemd Timer

The timer schedules the processor to run automatically every day.

### Create the timer file

```bash
sudo nano /etc/systemd/system/routetrack-processor.timer

```

### Timer used (daily at 2:10 AM local time)

```ini
[Unit]
Description=Run RouteTrack Processor Daily

[Timer]
OnCalendar=*-*-* 02:10:00
Persistent=true
RandomizedDelaySec=30
Unit=routetrack-processor.service

[Install]
WantedBy=timers.target

```

Reload and enable:

```bash
sudo systemctl daemon-reload
sudo systemctl enable --now routetrack-processor.timer

```

---

## Verify the Timer is Active

Show timers:

```bash
systemctl list-timers --all | grep routetrack

```

Check timer status:

```bash
systemctl status routetrack-processor.timer --no-pager -l

```

This confirms:

- Timer is **active (waiting)**
- Next trigger time is scheduled
- It triggers `routetrack-processor.service`

---

## Confirm Data Was Written

After a successful run, verify the summary table:

```bash
sqlite3 /opt/routetrack/data/routetrack.sqlite \
"SELECT * FROM daily_summary ORDER BY date DESC LIMIT 3;"

```

This confirms the processor populated:

- `daily_summary`
- `stop_events`

and is producing real computed metrics.

---

## Run On Demand (Manual Trigger Block)

This is the clean “run it right now” workflow (and verify it worked) without touching the timer schedule.

### Run the processor service now

```bash
sudo systemctl start routetrack-processor.service

```

### View the last 50 log lines from the run

```bash
journalctl -u routetrack-processor.service -n 50 --no-pager -l

```

### Confirm the next scheduled timer run is still set

```bash
systemctl list-timers --all | grep routetrack

```

---

## Operational Notes

### Why the logger must stop

SQLite needs exclusive access for derived-table regeneration (`DELETE` + `INSERT`).  
Running the processor while the logger is inserting can produce:

- `sqlite3.OperationalError: database is locked`

This wrapper workflow prevents that completely.

### Where logs live

Everything is stored in systemd journal:

```bash
journalctl -u routetrack-processor.service --since "today" --no-pager -l

```

---

## Next Steps

Now that processing is automated daily, the next phase is the dashboard:

- Flask web service (local)
- Leaflet map page
- Route drawing from `gps_points`
- Stop markers from `stop_events`
- Daily totals from `daily_summary`