Functional Scope (In-Scope)
- Physical IoT Unlock Cycles: Validates bike states atomically, triggers virtual hardware lock controllers, and tracks trip beginnings.
- Hybrid Dock/Dockless Parking Validators: Enforces returns within either physical station slots or virtual geofenced circles.
- Adaptive Per-Minute Billing Engine: Calculates trip cost base rates, running duration minutes, and caps charges at a daily maximum.
- Stateful Trip Management: Restricts users to one active bike booking at a time and logs completed coordinates.
Explicit Boundaries (Out-of-Scope)
- Dynamic Maintenance Dispatching: Focuses on on-trip state changes rather than rebalancing truck routing algorithms.
- Real-time GPS Tracking Channels: Excludes handling WebSockets or telemetry data feeds to focus on start/end coordinate validation.
Production reference implementations demonstrating IoT lock integrations, geofenced boundaries, and duration billing calculators in Java and Python:
// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
enum BikeStatus {
AVAILABLE, IN_USE, MAINTENANCE
}
enum TripStatus {
ACTIVE, COMPLETED, UNPAID
}
class Point {
private final double lat;
private final double lng;
public Point(double lat, double lng) {
this.lat = lat;
this.lng = lng;
}
public double getLat() { return lat; }
public double getLng() { return lng; }
public double distanceTo(Point other) {
double R = 6371000.0; // Earth's radius in meters
double latDist = Math.toRadians(other.lat - this.lat);
double lngDist = Math.toRadians(other.lng - this.lng);
double a = Math.sin(latDist / 2) * Math.sin(latDist / 2)
+ Math.cos(Math.toRadians(this.lat)) * Math.cos(Math.toRadians(other.lat))
* Math.sin(lngDist / 2) * Math.sin(lngDist / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c;
}
}
class Bike {
private final String bikeId;
private final AtomicReference<BikeStatus> status = new AtomicReference<>(BikeStatus.AVAILABLE);
private volatile Point location;
private volatile String currentDockId = null;
public Bike(String bikeId, Point initialLocation) {
this.bikeId = bikeId;
this.location = initialLocation;
}
public String getBikeId() { return bikeId; }
public BikeStatus getStatus() { return status.get(); }
public boolean compareAndSetStatus(BikeStatus expect, BikeStatus update) {
return status.compareAndSet(expect, update);
}
public void setStatus(BikeStatus s) { status.set(s); }
public Point getLocation() { return location; }
public void setLocation(Point location) { this.location = location; }
public String getCurrentDockId() { return currentDockId; }
public void setCurrentDockId(String currentDockId) { this.currentDockId = currentDockId; }
}
class Dock {
private final String dockId;
private final Point location;
private final int capacity;
private int occupiedSlots = 0;
public Dock(String dockId, Point location, int capacity) {
this.dockId = dockId;
this.location = location;
this.capacity = capacity;
}
public synchronized boolean hasAvailableSlots() {
return occupiedSlots < capacity;
}
public synchronized boolean incrementOccupied() {
if (occupiedSlots < capacity) {
occupiedSlots++;
return true;
}
return false;
}
public synchronized void decrementOccupied() {
if (occupiedSlots > 0) {
occupiedSlots--;
}
}
public String getDockId() { return dockId; }
public Point getLocation() { return location; }
public int getCapacity() { return capacity; }
public synchronized int getOccupiedSlots() { return occupiedSlots; }
}
class ParkingZone {
private final String zoneId;
private final Point center;
private final double radiusMeters;
public ParkingZone(String zoneId, Point center, double radiusMeters) {
this.zoneId = zoneId;
this.center = center;
this.radiusMeters = radiusMeters;
}
public boolean contains(Point point) {
return center.distanceTo(point) <= radiusMeters;
}
public String getZoneId() { return zoneId; }
}
class Trip {
private final String tripId;
private final String userId;
private final String bikeId;
private final long startTimeMs;
private final Point startLocation;
private volatile long endTimeMs = 0L;
private volatile Point endLocation = null;
private volatile double fare = 0.0;
private volatile TripStatus status = TripStatus.ACTIVE;
public Trip(String tripId, String userId, String bikeId, Point startLocation) {
this.tripId = tripId;
this.userId = userId;
this.bikeId = bikeId;
this.startTimeMs = System.currentTimeMillis();
this.startLocation = startLocation;
}
public void completeTrip(Point endLocation, double fare) {
this.endTimeMs = System.currentTimeMillis();
this.endLocation = endLocation;
this.fare = fare;
this.status = TripStatus.COMPLETED;
}
public String getTripId() { return tripId; }
public String getUserId() { return userId; }
public String getBikeId() { return bikeId; }
public long getStartTimeMs() { return startTimeMs; }
public Point getStartLocation() { return startLocation; }
public long getEndTimeMs() { return endTimeMs; }
public Point getEndLocation() { return endLocation; }
public double getFare() { return fare; }
public TripStatus getStatus() { return status; }
}
class BillingEngine {
private static final double BASE_FEE = 2.0;
private static final double RATE_PER_MINUTE = 0.15;
private static final double DAILY_MAX_CAP = 20.0;
public static double calculateFare(long startTimeMs, long endTimeMs) {
long durationMs = endTimeMs - startTimeMs;
if (durationMs < 0) durationMs = 0;
double durationMinutes = durationMs / 60000.0;
double charge = BASE_FEE + (durationMinutes * RATE_PER_MINUTE);
return Math.min(charge, DAILY_MAX_CAP);
}
}
class LockController {
// Simulates an IoT Lock gateway unlock command
public boolean sendUnlockSignal(String bikeId) {
System.out.println("IOT SIGNAL -> Unlocking Bike: " + bikeId);
return true; // Return successful hardware feedback
}
public boolean sendLockSignal(String bikeId) {
System.out.println("IOT SIGNAL -> Locking Bike: " + bikeId);
return true;
}
}
class BikeSharingService {
private final ConcurrentHashMap<String, Bike> bikes = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, Dock> docks = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, ParkingZone> geofences = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, Trip> activeTrips = new ConcurrentHashMap<>(); // userId -> Trip
private final ConcurrentHashMap<String, List<Trip>> userHistory = new ConcurrentHashMap<>();
private final LockController lockController = new LockController();
public void registerBike(Bike bike) {
bikes.put(bike.getBikeId(), bike);
}
public void registerDock(Dock dock) {
docks.put(dock.getDockId(), dock);
}
public void registerParkingZone(ParkingZone zone) {
geofences.put(zone.getZoneId(), zone);
}
// Dynamic Unlock Sequence
public Trip unlockBike(String userId, String bikeId) {
Bike bike = bikes.get(bikeId);
if (bike == null) throw new IllegalArgumentException("Bike does not exist!");
if (activeTrips.containsKey(userId)) {
throw new IllegalStateException("User already has an active trip!");
}
// Atomically transitions bike state
if (!bike.compareAndSetStatus(BikeStatus.AVAILABLE, BikeStatus.IN_USE)) {
throw new IllegalStateException("Bike is not available for booking!");
}
// Dispatch hardware signal
boolean hardwareSuccess = lockController.sendUnlockSignal(bikeId);
if (!hardwareSuccess) {
bike.setStatus(BikeStatus.AVAILABLE); // Rollback state
throw new RuntimeException("Hardware failed to unlock bike!");
}
// Release occupied slot if bike was in a dock
if (bike.getCurrentDockId() != null) {
Dock dock = docks.get(bike.getCurrentDockId());
if (dock != null) {
dock.decrementOccupied();
}
bike.setCurrentDockId(null);
}
String tripId = UUID.randomUUID().toString();
Trip trip = new Trip(tripId, userId, bikeId, bike.getLocation());
activeTrips.put(userId, trip);
System.out.println("TRIP STARTED -> User: " + userId + " | Trip: " + tripId + " | Bike: " + bikeId);
return trip;
}
// Dynamic Return & End Trip Sequence
public void lockBike(String userId, Point returnLocation) {
Trip trip = activeTrips.get(userId);
if (trip == null) {
throw new IllegalStateException("No active trip found for user!");
}
String bikeId = trip.getBikeId();
Bike bike = bikes.get(bikeId);
if (bike == null) return;
// Verify IoT lock feedback
boolean hardwareSuccess = lockController.sendLockSignal(bikeId);
if (!hardwareSuccess) {
throw new RuntimeException("Hardware failed to confirm lock status!");
}
// Parking zone verification (Dock or Dockless geofence)
boolean validParking = false;
String dockIdMatched = null;
// 1. Check if parked at a dock
for (Dock dock : docks.values()) {
if (dock.getLocation().distanceTo(returnLocation) <= 15.0) { // Within 15 meters
if (dock.incrementOccupied()) {
validParking = true;
dockIdMatched = dock.getDockId();
break;
}
}
}
// 2. Check if parked inside a dockless geofence zone
if (!validParking) {
for (ParkingZone zone : geofences.values()) {
if (zone.contains(returnLocation)) {
validParking = true;
break;
}
}
}
if (!validParking) {
System.out.println("OUT-OF-BOUNDS WARNING -> User parked outside permitted zone. Applying surcharge penalty!");
// Surcharges could be applied to fare calculations here
}
// Finalize state updates
long now = System.currentTimeMillis();
double fare = BillingEngine.calculateFare(trip.getStartTimeMs(), now);
trip.completeTrip(returnLocation, fare);
bike.setLocation(returnLocation);
bike.setCurrentDockId(dockIdMatched);
bike.setStatus(BikeStatus.AVAILABLE);
activeTrips.remove(userId);
userHistory.computeIfAbsent(userId, k -> new CopyOnWriteArrayList<>()).add(trip);
System.out.println("TRIP CONCLUDED -> User: " + userId + " | Fare: $" + fare + " | Parked Dock: " + (dockIdMatched != null ? dockIdMatched : "Dockless Zone"));
}
public List<Trip> getUserHistory(String userId) {
return userHistory.getOrDefault(userId, Collections.emptyList());
}
}
public class Main {
public static void main(String[] args) throws Exception {
System.out.println("=== JAVA BIKE SHARING SYSTEM SIMULATION ===");
BikeSharingService service = new BikeSharingService();
Point startLoc = new Point(40.7128, -74.0060);
Bike bike1 = new Bike("bike-101", startLoc);
service.registerBike(bike1);
Dock dock1 = new Dock("dock-001", new Point(40.7129, -74.0061), 5);
dock1.incrementOccupied(); // bike1 is in this dock initially
bike1.setCurrentDockId("dock-001");
service.registerDock(dock1);
ParkingZone zone1 = new ParkingZone("zone-south", new Point(40.7100, -74.0090), 100.0);
service.registerParkingZone(zone1);
// Unlock
Trip trip = service.unlockBike("user-1", "bike-101");
Thread.sleep(100); // simulate a tiny delay
// Return location (near the parking zone)
Point endLoc = new Point(40.7102, -74.0091);
service.lockBike("user-1", endLoc);
System.out.println("User trip count: " + service.getUserHistory("user-1").size());
System.out.println("=== END OF JAVA SIMULATION ===");
}
}