- Add guides: quick-start, installation, configuration, grower, consumer, transport, vertical-farm - Add API references: REST, demand, vertical-farming - Add concepts: blockchain, seasonal-planning, carbon-footprint - Add architecture: data-flow, transport-tracking - Add vertical-farming: environmental-control, automation, integration - Add examples: seed-to-harvest, demand-driven-planting, vertical-farm-setup Completes Agent_5 documentation tasks from AGENT_REPORT.md
489 lines
15 KiB
Markdown
489 lines
15 KiB
Markdown
# Transport Tracking Architecture
|
||
|
||
Deep dive into the seed-to-seed transport tracking system.
|
||
|
||
## Architecture Overview
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ TRANSPORT TRACKING SYSTEM │
|
||
├─────────────────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||
│ │ TRANSPORT CHAIN │ │
|
||
│ │ │ │
|
||
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
|
||
│ │ │Genesis │→ │ Block 1 │→ │ Block 2 │→ │ Block N │ │ │
|
||
│ │ │ │ │ Seed │ │ Plant │ │ Harvest │ │ │
|
||
│ │ │ │ │ Acquire │ │ │ │ │ │ │
|
||
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
|
||
│ │ │ │
|
||
│ └─────────────────────────────────────────────────────────────┘ │
|
||
│ │ │
|
||
│ ┌──────────────────┼──────────────────┐ │
|
||
│ ▼ ▼ ▼ │
|
||
│ ┌────────────────┐ ┌──────────────┐ ┌───────────────┐ │
|
||
│ │ Event Index │ │ Plant Index │ │ Batch Index │ │
|
||
│ │ (by event ID) │ │ (by plant ID)│ │ (by batch ID) │ │
|
||
│ └────────────────┘ └──────────────┘ └───────────────┘ │
|
||
│ │
|
||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||
│ │ SERVICES │ │
|
||
│ │ │ │
|
||
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
|
||
│ │ │Distance │ │ Carbon │ │ QR │ │ Journey │ │ │
|
||
│ │ │Calculator│ │Calculator│ │Generator│ │ Builder │ │ │
|
||
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
|
||
│ │ │ │
|
||
│ └─────────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
## TransportChain Class
|
||
|
||
### Core Structure
|
||
|
||
```typescript
|
||
class TransportChain {
|
||
// Blockchain data
|
||
public chain: TransportBlock[];
|
||
public difficulty: number;
|
||
|
||
// Indexes for fast lookups
|
||
private eventIndex: Map<string, TransportBlock[]>;
|
||
private plantEvents: Map<string, TransportEvent[]>;
|
||
private batchEvents: Map<string, TransportEvent[]>;
|
||
|
||
// Core operations
|
||
recordEvent(event: TransportEvent): TransportBlock;
|
||
getPlantJourney(plantId: string): PlantJourney | null;
|
||
getEnvironmentalImpact(userId: string): EnvironmentalImpact;
|
||
generateQRData(plantId?: string, batchId?: string): TransportQRData;
|
||
isChainValid(): boolean;
|
||
}
|
||
```
|
||
|
||
### Block Creation Flow
|
||
|
||
```
|
||
Event Input
|
||
│
|
||
▼
|
||
┌─────────────────────┐
|
||
│ Calculate Distance │ (if not provided)
|
||
│ Haversine Formula │
|
||
└──────────┬──────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────┐
|
||
│ Calculate Carbon │ (if not provided)
|
||
│ factor × distance │ × weight
|
||
└──────────┬──────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────┐
|
||
│ Create Block │
|
||
│ - index │
|
||
│ - timestamp │
|
||
│ - event data │
|
||
│ - previousHash │
|
||
│ - cumulative stats │
|
||
└──────────┬──────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────┐
|
||
│ Mine Block │
|
||
│ Find nonce where │
|
||
│ hash starts with │
|
||
│ N zeros │
|
||
└──────────┬──────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────┐
|
||
│ Add to Chain │
|
||
│ Update Indexes │
|
||
└──────────┬──────────┘
|
||
│
|
||
▼
|
||
Return Block
|
||
```
|
||
|
||
## Event Processing
|
||
|
||
### Event Type Handlers
|
||
|
||
Each event type extracts different IDs for indexing:
|
||
|
||
```typescript
|
||
function extractPlantIds(event: TransportEvent): string[] {
|
||
switch (event.eventType) {
|
||
case 'planting':
|
||
return event.plantIds;
|
||
|
||
case 'growing_transport':
|
||
return event.plantIds;
|
||
|
||
case 'harvest':
|
||
return event.plantIds;
|
||
|
||
case 'seed_saving':
|
||
return event.parentPlantIds;
|
||
|
||
default:
|
||
return [];
|
||
}
|
||
}
|
||
|
||
function extractBatchIds(event: TransportEvent): string[] {
|
||
switch (event.eventType) {
|
||
case 'seed_acquisition':
|
||
return [event.seedBatchId];
|
||
|
||
case 'planting':
|
||
return [event.seedBatchId];
|
||
|
||
case 'harvest':
|
||
const ids = [event.harvestBatchId];
|
||
if (event.seedBatchIdCreated) ids.push(event.seedBatchIdCreated);
|
||
return ids;
|
||
|
||
case 'processing':
|
||
return [...event.harvestBatchIds, event.processingBatchId];
|
||
|
||
case 'distribution':
|
||
case 'consumer_delivery':
|
||
return event.batchIds;
|
||
|
||
case 'seed_saving':
|
||
return [event.newSeedBatchId];
|
||
|
||
case 'seed_sharing':
|
||
return [event.seedBatchId];
|
||
|
||
default:
|
||
return [];
|
||
}
|
||
}
|
||
```
|
||
|
||
## Distance Calculation
|
||
|
||
### Haversine Formula
|
||
|
||
```typescript
|
||
static calculateDistance(
|
||
from: TransportLocation,
|
||
to: TransportLocation
|
||
): number {
|
||
const R = 6371; // Earth's radius in km
|
||
|
||
const dLat = toRadians(to.latitude - from.latitude);
|
||
const dLon = toRadians(to.longitude - from.longitude);
|
||
|
||
const a =
|
||
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
||
Math.cos(toRadians(from.latitude)) *
|
||
Math.cos(toRadians(to.latitude)) *
|
||
Math.sin(dLon / 2) * Math.sin(dLon / 2);
|
||
|
||
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||
|
||
return R * c; // Distance in kilometers
|
||
}
|
||
```
|
||
|
||
### Accuracy Notes
|
||
|
||
| Distance | Accuracy |
|
||
|----------|----------|
|
||
| < 10 km | ± 10 meters |
|
||
| 10-100 km | ± 100 meters |
|
||
| 100-1000 km | ± 1 km |
|
||
| > 1000 km | ± 10 km |
|
||
|
||
## Carbon Calculation
|
||
|
||
### Weight Estimation
|
||
|
||
```typescript
|
||
function estimateWeight(event: TransportEvent): number {
|
||
switch (event.eventType) {
|
||
case 'seed_acquisition':
|
||
case 'seed_saving':
|
||
case 'seed_sharing':
|
||
return 0.1; // Seeds are light
|
||
|
||
case 'planting':
|
||
return 0.5; // Seedlings
|
||
|
||
case 'growing_transport':
|
||
return 2; // Potted plants
|
||
|
||
case 'harvest':
|
||
return event.netWeight || 5;
|
||
|
||
case 'processing':
|
||
return event.outputWeight || 5;
|
||
|
||
case 'distribution':
|
||
case 'consumer_delivery':
|
||
return 5; // Default produce weight
|
||
|
||
default:
|
||
return 1;
|
||
}
|
||
}
|
||
```
|
||
|
||
### Carbon Formula
|
||
|
||
```typescript
|
||
function calculateCarbon(
|
||
method: TransportMethod,
|
||
distanceKm: number,
|
||
weightKg: number
|
||
): number {
|
||
const factor = CARBON_FACTORS[method] || 0.1;
|
||
return factor * distanceKm * weightKg;
|
||
}
|
||
```
|
||
|
||
## Journey Reconstruction
|
||
|
||
### Algorithm
|
||
|
||
```typescript
|
||
function getPlantJourney(plantId: string): PlantJourney | null {
|
||
// 1. Get all events for this plant
|
||
const events = plantEvents.get(plantId);
|
||
if (!events || events.length === 0) return null;
|
||
|
||
// 2. Sort by timestamp
|
||
const sorted = [...events].sort(
|
||
(a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
|
||
);
|
||
|
||
// 3. Calculate totals
|
||
let totalFoodMiles = 0;
|
||
let totalCarbonKg = 0;
|
||
let daysInTransit = 0;
|
||
|
||
for (const event of sorted) {
|
||
totalFoodMiles += event.distanceKm;
|
||
totalCarbonKg += event.carbonFootprintKg;
|
||
daysInTransit += event.durationMinutes / (60 * 24);
|
||
}
|
||
|
||
// 4. Determine current stage
|
||
const currentStage = determineStage(sorted);
|
||
|
||
// 5. Find lineage info
|
||
const seedAcquisition = sorted.find(e => e.eventType === 'seed_acquisition');
|
||
const planting = sorted.find(e => e.eventType === 'planting');
|
||
|
||
// 6. Build journey object
|
||
return {
|
||
plantId,
|
||
seedBatchOrigin: seedAcquisition?.seedBatchId || planting?.seedBatchId,
|
||
currentCustodian: sorted[sorted.length - 1].receiverId,
|
||
currentLocation: sorted[sorted.length - 1].toLocation,
|
||
currentStage,
|
||
events: sorted,
|
||
totalFoodMiles,
|
||
totalCarbonKg,
|
||
daysInTransit: Math.round(daysInTransit),
|
||
daysGrowing: calculateGrowingDays(sorted),
|
||
generation: seedAcquisition?.generation || 0,
|
||
ancestorPlantIds: seedAcquisition?.parentPlantIds || [],
|
||
descendantSeedBatches: findDescendantSeeds(sorted)
|
||
};
|
||
}
|
||
```
|
||
|
||
## QR Code Generation
|
||
|
||
### Data Structure
|
||
|
||
```typescript
|
||
interface TransportQRData {
|
||
plantId?: string;
|
||
batchId?: string;
|
||
blockchainAddress: string; // First 42 chars of latest hash
|
||
quickLookupUrl: string; // Web URL for tracking
|
||
lineageHash: string; // SHA256 of all events (16 chars)
|
||
currentCustodian: string; // Current holder
|
||
lastEventType: TransportEventType;
|
||
lastEventTimestamp: string;
|
||
verificationCode: string; // Random 8-char code
|
||
}
|
||
```
|
||
|
||
### Generation Process
|
||
|
||
```
|
||
┌─────────────────────┐
|
||
│ Get events for │
|
||
│ plant/batch │
|
||
└──────────┬──────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────┐
|
||
│ Calculate lineage │
|
||
│ hash (SHA256) │
|
||
└──────────┬──────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────┐
|
||
│ Get blockchain │
|
||
│ address (latest │
|
||
│ block hash) │
|
||
└──────────┬──────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────┐
|
||
│ Generate │
|
||
│ verification code │
|
||
└──────────┬──────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────┐
|
||
│ Build QR data │
|
||
│ object │
|
||
└──────────┬──────────┘
|
||
│
|
||
▼
|
||
Return QRData
|
||
```
|
||
|
||
## Chain Validation
|
||
|
||
### Validation Checks
|
||
|
||
```typescript
|
||
function isChainValid(): boolean {
|
||
for (let i = 1; i < chain.length; i++) {
|
||
const current = chain[i];
|
||
const previous = chain[i - 1];
|
||
|
||
// Check 1: Hash integrity
|
||
const expectedHash = calculateHash(
|
||
current.index,
|
||
current.timestamp,
|
||
current.transportEvent,
|
||
current.previousHash,
|
||
current.nonce
|
||
);
|
||
if (current.hash !== expectedHash) {
|
||
return false; // Hash has been tampered
|
||
}
|
||
|
||
// Check 2: Chain linkage
|
||
if (current.previousHash !== previous.hash) {
|
||
return false; // Chain is broken
|
||
}
|
||
|
||
// Check 3: Proof of work
|
||
const target = '0'.repeat(difficulty);
|
||
if (current.hash.substring(0, difficulty) !== target) {
|
||
return false; // PoW not satisfied
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
```
|
||
|
||
## Storage and Persistence
|
||
|
||
### Export Format
|
||
|
||
```json
|
||
{
|
||
"difficulty": 3,
|
||
"chain": [
|
||
{
|
||
"index": 0,
|
||
"timestamp": "2024-01-01T00:00:00Z",
|
||
"transportEvent": { ... },
|
||
"previousHash": "0",
|
||
"hash": "000abc...",
|
||
"nonce": 12345,
|
||
"cumulativeCarbonKg": 0,
|
||
"cumulativeFoodMiles": 0,
|
||
"chainLength": 1
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### Import Process
|
||
|
||
```typescript
|
||
static fromJSON(data: any): TransportChain {
|
||
const chain = new TransportChain(data.difficulty);
|
||
chain.chain = data.chain;
|
||
|
||
// Rebuild indexes from chain
|
||
for (const block of chain.chain) {
|
||
chain.indexEvent(block.transportEvent, block);
|
||
}
|
||
|
||
return chain;
|
||
}
|
||
```
|
||
|
||
## Performance Optimization
|
||
|
||
### Indexing Strategy
|
||
|
||
| Index | Use Case | Complexity |
|
||
|-------|----------|------------|
|
||
| eventIndex | Event lookup by ID | O(1) |
|
||
| plantEvents | Plant journey | O(1) |
|
||
| batchEvents | Batch tracking | O(1) |
|
||
|
||
### Mining Optimization
|
||
|
||
```typescript
|
||
// Parallel mining hint (future)
|
||
// Can be parallelized across CPU cores
|
||
function mineBlockParallel(block: TransportBlock): void {
|
||
const workers = os.cpus().length;
|
||
const ranges = divideNonceRange(0, MAX_NONCE, workers);
|
||
|
||
// Each worker tries nonces in its range
|
||
// First to find valid hash wins
|
||
}
|
||
```
|
||
|
||
## Integration Points
|
||
|
||
### With Plant Blockchain
|
||
|
||
```
|
||
Transport Chain Plant Chain
|
||
────────────── ───────────
|
||
plantIds in events ←─────────→ plantId in registrations
|
||
harvestBatchId ←─────────→ linked to plant
|
||
seedBatchId ←─────────→ lineage reference
|
||
```
|
||
|
||
### With Vertical Farm Controller
|
||
|
||
```
|
||
VF Controller Transport Chain
|
||
───────────── ───────────────
|
||
startCropBatch() ─creates─→ planting event
|
||
completeHarvest() ─creates─→ harvest event
|
||
─creates─→ distribution event
|
||
```
|
||
|
||
### With Demand Forecaster
|
||
|
||
```
|
||
Demand Forecaster Transport Chain
|
||
───────────────── ───────────────
|
||
supply matching ─tracks─→ distribution events
|
||
market matches ─creates─→ consumer_delivery events
|
||
```
|