Merge pull request #2 from vespo92/claude/document-seed-transport-01TryFGuw6ViiwJfk1z9GWvR

Document seed-to-seed app transport system
This commit is contained in:
Vinnie Esposito 2025-11-22 12:25:37 -06:00 committed by GitHub
commit b8a3ebb823
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 5947 additions and 0 deletions

608
AGENT_REPORT.md Normal file
View file

@ -0,0 +1,608 @@
# LocalGreenChain Agent Report
## Implementation Summary
This document outlines the comprehensive implementation of LocalGreenChain's seed-to-seed transport tracking system, demand-driven agriculture platform, and vertical farming integration. After merging this PR, **5 specialized agents** can be dispatched in parallel to continue development without overlapping concerns.
---
## What Was Implemented
### 1. Seed-to-Seed Transport Tracking System
**Files Created:**
- `lib/transport/types.ts` - Complete type definitions for all transport events
- `lib/transport/tracker.ts` - TransportChain blockchain implementation
**Features:**
- 9 transport event types covering full seed-to-seed lifecycle
- Carbon footprint calculation per transport method
- Food miles tracking with Haversine distance calculation
- QR code generation for traceability
- Cross-chain reference support for plant transfers
- Environmental impact comparison vs conventional agriculture
### 2. Demand Forecasting System
**Files Created:**
- `lib/demand/types.ts` - Consumer preferences, demand signals, recommendations
- `lib/demand/forecaster.ts` - DemandForecaster with ML-ready architecture
**Features:**
- Consumer preference registration and aggregation
- Regional demand signal generation
- Supply gap identification
- Grower planting recommendations with risk assessment
- Seasonal planning integration
- Market matching between supply and demand
### 3. Vertical Farming Module
**Files Created:**
- `lib/vertical-farming/types.ts` - Complete VF facility, zone, and system types
- `lib/vertical-farming/controller.ts` - VerticalFarmController with recipes
**Features:**
- Multi-zone facility management
- Environmental control systems (HVAC, CO2, humidity, lighting)
- Irrigation and nutrient delivery systems
- Growing recipes with stage-based environment targets
- Crop batch tracking with health scoring
- Analytics generation (yield, efficiency, financials)
- Default recipes for lettuce, basil, and microgreens
### 4. Documentation Structure
**Created `/docs/` folder with:**
- `README.md` - Documentation index and project overview
- `concepts/seed-to-seed-transport.md` - Complete transport system documentation
- `concepts/demand-forecasting.md` - Demand-driven agriculture explanation
- `architecture/system-overview.md` - Full system architecture
- `architecture/user-blockchain.md` - Per-user blockchain design
- `api/transport-api.md` - Complete API reference
- `vertical-farming/README.md` - Vertical farming integration guide
---
## System Architecture
```
┌─────────────────────────────────────────────────────────────────────┐
│ LOCALGREENCHAIN PLATFORM │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌───────────┐│
│ │ TRANSPORT │ │ DEMAND │ │ VERTICAL │ │ PLANT ││
│ │ TRACKER │ │ FORECASTER │ │ FARM │ │ BLOCKCHAIN││
│ │ │ │ │ │ CONTROLLER │ │ ││
│ │ - Events │ │ - Prefs │ │ - Zones │ │ - Lineage ││
│ │ - Carbon │ │ - Signals │ │ - Recipes │ │ - Registry││
│ │ - QR Codes │ │ - Forecasts │ │ - Analytics │ │ - Privacy ││
│ └─────────────┘ └─────────────┘ └─────────────┘ └───────────┘│
│ │ │ │ │ │
│ └────────────────┴────────────────┴────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ USER BLOCKCHAIN │ │
│ │ Per-user chain │ │
│ │ with cross-refs│ │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
```
---
# AGENT TASKS
After this PR is merged, dispatch the following 5 agents **in parallel**. Each agent has a self-contained scope with no dependencies on other agents' work.
---
## Agent_1: API Routes Implementation
**Scope:** Create all API route handlers for the new transport, demand, and vertical farming systems.
### Task Description
Create the following API route files in `/pages/api/`:
```
pages/api/
├── transport/
│ ├── seed-acquisition.ts # POST - Record seed acquisition
│ ├── planting.ts # POST - Record planting event
│ ├── growing.ts # POST - Record growing transport
│ ├── harvest.ts # POST - Record harvest event
│ ├── distribution.ts # POST - Record distribution
│ ├── seed-saving.ts # POST - Record seed saving
│ ├── seed-sharing.ts # POST - Record seed sharing
│ ├── journey/[plantId].ts # GET - Get plant journey
│ ├── footprint/[userId].ts # GET - Get environmental impact
│ ├── verify/[blockHash].ts # GET - Verify block integrity
│ └── qr/[id].ts # GET - Generate QR code data
├── demand/
│ ├── preferences.ts # POST/GET - Consumer preferences
│ ├── signal.ts # POST - Generate demand signal
│ ├── recommendations.ts # GET - Get planting recommendations
│ ├── forecast.ts # GET - Get demand forecast
│ ├── supply.ts # POST - Register supply commitment
│ └── match.ts # POST - Create market match
└── vertical-farm/
├── register.ts # POST - Register new farm
├── [farmId]/
│ ├── index.ts # GET - Get farm details
│ ├── zones.ts # GET/POST - Manage zones
│ └── analytics.ts # GET - Get farm analytics
├── batch/
│ ├── start.ts # POST - Start crop batch
│ ├── [batchId]/
│ │ ├── index.ts # GET - Get batch details
│ │ ├── environment.ts # PUT - Record environment
│ │ └── harvest.ts # POST - Complete harvest
└── recipes.ts # GET - List growing recipes
```
### Implementation Guidelines
1. Follow existing API patterns in `/pages/api/plants/`
2. Use the imported types from `/lib/transport/types.ts`, `/lib/demand/types.ts`, `/lib/vertical-farming/types.ts`
3. Use singleton managers: `getTransportChain()`, `getDemandForecaster()`, `getVerticalFarmController()`
4. Return consistent JSON responses with `success`, `data`, and `error` fields
5. Add input validation for all POST endpoints
6. Include proper HTTP status codes (201 for creates, 200 for success, 400/404/500 for errors)
### Files to Read First
- `/lib/transport/tracker.ts` - TransportChain class and methods
- `/lib/demand/forecaster.ts` - DemandForecaster class and methods
- `/lib/vertical-farming/controller.ts` - VerticalFarmController class and methods
- `/pages/api/plants/register.ts` - Example API pattern
### Acceptance Criteria
- All endpoints functional and returning proper responses
- Error handling for invalid inputs
- TypeScript types properly used
---
## Agent_2: UI Components for Transport & Demand
**Scope:** Create React components for transport tracking and demand visualization.
### Task Description
Create the following components in `/components/`:
```
components/
├── transport/
│ ├── TransportTimeline.tsx # Visual timeline of transport events
│ ├── JourneyMap.tsx # Map showing plant journey
│ ├── CarbonFootprintCard.tsx # Display carbon metrics
│ ├── QRCodeDisplay.tsx # Render and download QR codes
│ └── TransportEventForm.tsx # Form for recording events
├── demand/
│ ├── DemandSignalCard.tsx # Show regional demand signal
│ ├── PreferencesForm.tsx # Consumer preference input
│ ├── RecommendationList.tsx # Planting recommendations
│ ├── SupplyGapChart.tsx # Visualize supply vs demand
│ └── SeasonalCalendar.tsx # Seasonal availability view
└── analytics/
├── EnvironmentalImpact.tsx # Carbon/miles comparison
├── FoodMilesTracker.tsx # Track food miles over time
└── SavingsCalculator.tsx # Show savings vs conventional
```
### Implementation Guidelines
1. Use existing component patterns from `/components/`
2. Use Tailwind CSS for styling (project uses Tailwind)
3. Components should be reusable and accept props
4. Include loading states and error handling
5. Use React Query patterns for data fetching (see existing components)
6. Make components responsive (mobile-first)
### Component Specifications
**TransportTimeline.tsx:**
```typescript
interface Props {
plantId: string;
events: TransportEvent[];
}
// Display vertical timeline with icons per event type
// Show date, location, carbon footprint for each event
```
**DemandSignalCard.tsx:**
```typescript
interface Props {
signal: DemandSignal;
onViewDetails?: () => void;
}
// Show region, demand items, supply status
// Color-coded status (green=surplus, yellow=balanced, red=shortage)
```
**CarbonFootprintCard.tsx:**
```typescript
interface Props {
impact: EnvironmentalImpact;
showComparison?: boolean;
}
// Display total carbon, food miles
// Optional comparison chart vs conventional
```
### Files to Read First
- `/components/EnvironmentalDisplay.tsx` - Example display component
- `/components/EnvironmentalForm.tsx` - Example form component
- `/lib/transport/types.ts` - Transport types
- `/lib/demand/types.ts` - Demand types
### Acceptance Criteria
- All components render correctly
- Props are properly typed
- Responsive design works on mobile
- Consistent styling with existing components
---
## Agent_3: Vertical Farming UI & Pages
**Scope:** Create complete UI pages and components for vertical farm management.
### Task Description
Create the following pages and components:
```
pages/
├── vertical-farm/
│ ├── index.tsx # List all farms / dashboard
│ ├── register.tsx # Register new farm form
│ ├── [farmId]/
│ │ ├── index.tsx # Farm detail view
│ │ ├── zones.tsx # Zone management
│ │ ├── batches.tsx # Active crop batches
│ │ └── analytics.tsx # Farm analytics
components/
├── vertical-farm/
│ ├── FarmCard.tsx # Farm summary card
│ ├── ZoneGrid.tsx # Grid display of zones
│ ├── ZoneDetailCard.tsx # Individual zone details
│ ├── EnvironmentGauge.tsx # Real-time env readings
│ ├── BatchProgress.tsx # Crop batch progress bar
│ ├── RecipeSelector.tsx # Select growing recipe
│ ├── AlertPanel.tsx # Environment alerts display
│ ├── GrowthStageIndicator.tsx # Visual stage progress
│ └── ResourceUsageChart.tsx # Energy/water usage chart
```
### Implementation Guidelines
1. Follow existing page patterns from `/pages/plants/`
2. Use the VerticalFarmController API
3. Include real-time updates for environment readings (polling or websocket)
4. Dashboard should show key metrics at a glance
5. Zone management should allow starting batches and monitoring progress
### Page Specifications
**index.tsx (Dashboard):**
- List user's farms with key stats
- Quick actions: Add farm, View analytics
- Summary cards showing total plants, upcoming harvests
**[farmId]/index.tsx (Farm Detail):**
- Farm info and specs
- Zone overview with status colors
- Current alerts
- Quick navigation to zones and batches
**[farmId]/zones.tsx (Zone Management):**
- Grid of zones with current crop info
- Click zone to expand details
- Start new batch action
- Environment readings
**[farmId]/analytics.tsx (Analytics):**
- Yield over time chart
- Efficiency metrics
- Top performing crops
- Resource usage trends
### Files to Read First
- `/lib/vertical-farming/types.ts` - All VF types
- `/lib/vertical-farming/controller.ts` - Controller methods
- `/pages/plants/explore.tsx` - Example page pattern
- `/docs/vertical-farming/README.md` - VF documentation
### Acceptance Criteria
- All pages functional with proper routing
- Data fetches from API endpoints
- Forms validate input properly
- Visual feedback for all actions
---
## Agent_4: Testing Suite
**Scope:** Create comprehensive tests for all new modules.
### Task Description
Create tests for transport, demand, and vertical farming systems:
```
__tests__/ (or tests/)
├── lib/
│ ├── transport/
│ │ ├── tracker.test.ts # TransportChain tests
│ │ ├── carbon.test.ts # Carbon calculation tests
│ │ └── types.test.ts # Type validation tests
│ ├── demand/
│ │ ├── forecaster.test.ts # DemandForecaster tests
│ │ ├── aggregation.test.ts # Demand aggregation tests
│ │ └── recommendations.test.ts # Recommendation logic
│ └── vertical-farming/
│ ├── controller.test.ts # VerticalFarmController tests
│ ├── recipes.test.ts # Recipe stage logic
│ └── environment.test.ts # Environment alert tests
├── api/
│ ├── transport.test.ts # Transport API tests
│ ├── demand.test.ts # Demand API tests
│ └── vertical-farm.test.ts # VF API tests
└── integration/
├── seed-to-seed.test.ts # Full lifecycle test
├── demand-to-harvest.test.ts # Demand → Plant → Harvest
└── vf-batch-lifecycle.test.ts # Complete VF batch test
```
### Test Specifications
**TransportChain Tests:**
```typescript
describe('TransportChain', () => {
it('should create genesis block on initialization');
it('should record seed acquisition event');
it('should calculate carbon footprint correctly');
it('should track plant journey across events');
it('should generate valid QR data');
it('should verify chain integrity');
it('should reject invalid events');
});
```
**DemandForecaster Tests:**
```typescript
describe('DemandForecaster', () => {
it('should register consumer preferences');
it('should aggregate demand by region');
it('should calculate supply gaps');
it('should generate planting recommendations');
it('should apply seasonal factors');
it('should assess risk factors');
});
```
**VerticalFarmController Tests:**
```typescript
describe('VerticalFarmController', () => {
it('should register a vertical farm');
it('should start crop batch with recipe');
it('should update batch progress');
it('should detect environment alerts');
it('should complete harvest and record yield');
it('should generate accurate analytics');
});
```
**Integration Tests:**
```typescript
describe('Seed-to-Seed Lifecycle', () => {
it('should track complete lifecycle from seed acquisition to seed saving');
// Create seed batch → Plant → Grow → Harvest → Save seeds → New generation
});
```
### Implementation Guidelines
1. Use Jest or the existing test framework (check `package.json`)
2. Mock external services (geolocation, plants.net API)
3. Test edge cases (empty data, invalid inputs, boundary conditions)
4. Include performance tests for blockchain operations
5. Ensure test isolation (no shared state between tests)
### Files to Read First
- `/lib/transport/tracker.ts`
- `/lib/demand/forecaster.ts`
- `/lib/vertical-farming/controller.ts`
- Existing tests in `/cypress/` for patterns
### Acceptance Criteria
- All tests pass
- Coverage > 80% for new code
- Edge cases covered
- Integration tests demonstrate full workflows
---
## Agent_5: Documentation & Guides Completion
**Scope:** Complete all remaining documentation, guides, and examples.
### Task Description
Complete the documentation structure:
```
docs/
├── guides/
│ ├── quick-start.md # 5-minute getting started
│ ├── installation.md # Detailed installation
│ ├── configuration.md # Environment variables, settings
│ ├── grower-guide.md # Complete grower workflow
│ ├── consumer-guide.md # Consumer usage guide
│ ├── transport-guide.md # Transport logging guide
│ └── vertical-farm-guide.md # VF operator guide
├── api/
│ ├── rest-api.md # Full REST API reference
│ ├── demand-api.md # Demand API reference
│ └── vertical-farming-api.md # VF API reference
├── concepts/
│ ├── blockchain.md # Blockchain explained
│ ├── seasonal-planning.md # Seasonal optimization
│ └── carbon-footprint.md # Carbon tracking explained
├── architecture/
│ ├── data-flow.md # Data flow diagrams
│ └── transport-tracking.md # Transport architecture
├── vertical-farming/
│ ├── environmental-control.md # Env control details
│ ├── automation.md # Automation systems
│ └── integration.md # Integration guide
└── examples/
├── seed-to-harvest.md # Example workflow
├── demand-driven-planting.md # Demand example
└── vertical-farm-setup.md # VF setup example
```
### Documentation Requirements
**quick-start.md:**
- Prerequisites (Bun, etc.)
- Clone, install, run in < 5 minutes
- First plant registration
- View your plant on the network
**grower-guide.md:**
- Setting up as a grower
- Registering plants
- Recording transport events
- Responding to demand signals
- Harvesting and seed saving
- Analytics and optimization
**consumer-guide.md:**
- Setting preferences
- Finding local produce
- Scanning QR codes
- Understanding plant history
- Providing feedback
**vertical-farm-guide.md:**
- Facility registration
- Zone configuration
- Recipe selection
- Batch management
- Environment monitoring
- Analytics interpretation
### Code Examples
Include working code examples:
```typescript
// Example: Register a plant and track transport
const plant = await registerPlant({
commonName: 'Tomato',
scientificName: 'Solanum lycopersicum',
// ...
});
await recordTransport({
eventType: 'seed_acquisition',
seedBatchId: 'batch-001',
// ...
});
```
### Diagram Requirements
Create ASCII diagrams for:
1. Complete data flow from seed to seed
2. Demand signal aggregation process
3. Vertical farm batch lifecycle
4. Carbon footprint calculation flow
### Files to Read First
- `/docs/README.md` - Existing doc structure
- `/docs/concepts/seed-to-seed-transport.md` - Transport docs
- `/docs/vertical-farming/README.md` - VF docs
- `/README.md` - Main project README
### Acceptance Criteria
- All guide files created and complete
- Code examples are accurate and tested
- Diagrams are clear and accurate
- Cross-references between docs work
- Consistent formatting and style
---
## Parallel Execution Notes
These 5 agents can run **completely in parallel** because:
1. **Agent_1 (API)**: Works only in `/pages/api/` - no overlap with UI
2. **Agent_2 (UI Components)**: Creates `/components/` only - no overlap with pages
3. **Agent_3 (VF Pages)**: Creates `/pages/vertical-farm/` - separate from components
4. **Agent_4 (Tests)**: Creates `/__tests__/` - completely separate directory
5. **Agent_5 (Docs)**: Works only in `/docs/` - no code changes
### Execution Command
After PR merge, dispatch all 5 agents with:
```
Agent_1: "Read AGENT_REPORT.md section 'Agent_1: API Routes Implementation' and complete all tasks"
Agent_2: "Read AGENT_REPORT.md section 'Agent_2: UI Components for Transport & Demand' and complete all tasks"
Agent_3: "Read AGENT_REPORT.md section 'Agent_3: Vertical Farming UI & Pages' and complete all tasks"
Agent_4: "Read AGENT_REPORT.md section 'Agent_4: Testing Suite' and complete all tasks"
Agent_5: "Read AGENT_REPORT.md section 'Agent_5: Documentation & Guides Completion' and complete all tasks"
```
---
## Summary of New Files
### Library Files (Core Implementation)
| File | Purpose |
|------|---------|
| `lib/transport/types.ts` | Transport event type definitions |
| `lib/transport/tracker.ts` | TransportChain blockchain |
| `lib/demand/types.ts` | Demand forecasting types |
| `lib/demand/forecaster.ts` | DemandForecaster implementation |
| `lib/vertical-farming/types.ts` | Vertical farm types |
| `lib/vertical-farming/controller.ts` | VerticalFarmController |
### Documentation Files
| File | Purpose |
|------|---------|
| `docs/README.md` | Documentation index |
| `docs/concepts/seed-to-seed-transport.md` | Transport system docs |
| `docs/concepts/demand-forecasting.md` | Demand system docs |
| `docs/architecture/system-overview.md` | Architecture overview |
| `docs/architecture/user-blockchain.md` | Per-user blockchain design |
| `docs/api/transport-api.md` | Transport API reference |
| `docs/vertical-farming/README.md` | Vertical farming guide |
---
## Environmental Impact Goals
This implementation enables LocalGreenChain to achieve:
| Metric | Target | How |
|--------|--------|-----|
| Food Miles Reduction | 97% | Local production + vertical farms |
| Carbon Reduction | 89% | Short transport + clean energy |
| Water Reduction | 90% | Vertical farm efficiency |
| Food Waste Reduction | 75% | Demand-driven production |
| Year-Round Availability | 100% | Vertical farm integration |
---
*This report was generated as part of the LocalGreenChain seed-to-seed transport implementation. For questions, see the documentation or open an issue.*

99
docs/README.md Normal file
View file

@ -0,0 +1,99 @@
# LocalGreenChain Documentation
> **A demand-driven, seasonal agriculture platform leveraging blockchain technology to track plant lineage from seed-to-seed while reducing the global footprint of human consumption.**
## Vision
LocalGreenChain transforms agriculture by enabling:
- **Demand-Driven Production**: Grow what consumers need, when they need it
- **Seasonal Optimization**: Align production with natural growing cycles
- **Footprint Reduction**: Minimize transportation, waste, and resource consumption
- **Complete Traceability**: Track every plant from seed origin to harvest and back to seed
## Documentation Index
### Getting Started
- [Quick Start Guide](./guides/quick-start.md)
- [Installation](./guides/installation.md)
- [Configuration](./guides/configuration.md)
### Core Concepts
- [Seed-to-Seed Transport System](./concepts/seed-to-seed-transport.md)
- [Blockchain Architecture](./concepts/blockchain.md)
- [Demand Forecasting](./concepts/demand-forecasting.md)
- [Seasonal Planning](./concepts/seasonal-planning.md)
- [Carbon Footprint Tracking](./concepts/carbon-footprint.md)
### Architecture
- [System Overview](./architecture/system-overview.md)
- [Data Flow](./architecture/data-flow.md)
- [User Blockchain](./architecture/user-blockchain.md)
- [Transport Tracking](./architecture/transport-tracking.md)
### Vertical Farming
- [Overview](./vertical-farming/README.md)
- [Environmental Control](./vertical-farming/environmental-control.md)
- [Automation Systems](./vertical-farming/automation.md)
- [Integration Guide](./vertical-farming/integration.md)
### API Reference
- [REST API](./api/rest-api.md)
- [Transport API](./api/transport-api.md)
- [Demand API](./api/demand-api.md)
- [Vertical Farming API](./api/vertical-farming-api.md)
### Guides
- [Grower Guide](./guides/grower-guide.md)
- [Consumer Guide](./guides/consumer-guide.md)
- [Transport Guide](./guides/transport-guide.md)
- [Vertical Farm Operator Guide](./guides/vertical-farm-guide.md)
## Project Goals
### Environmental Impact Reduction
```
Traditional Supply Chain:
[Seed] → [Farm] → [Processor] → [Distributor] → [Retailer] → [Consumer]
↓ ↓ ↓ ↓
Waste Transport Storage Spoilage
(30%) Emissions Energy (15%)
```
```
LocalGreenChain Model:
[Seed] ← → [Local Grower] ← → [Consumer]
[Vertical Farm]
Demand-Optimized
Zero-Waste Cycle
```
### Key Metrics We Track
| Metric | Traditional | LocalGreenChain Target |
|--------|-------------|----------------------|
| Food Miles | 1,500+ avg | < 50 miles |
| Spoilage Rate | 30-40% | < 5% |
| Carbon/lb | 2.5 kg CO2 | 0.3 kg CO2 |
| Water Usage | 100% | 10% (vertical) |
| Pesticide Use | Variable | Zero (controlled) |
## Technology Stack
- **Runtime**: Bun (fast JavaScript runtime)
- **Frontend**: Next.js, React, TypeScript, Tailwind CSS
- **Blockchain**: Custom proof-of-work implementation
- **Transport Tracking**: GPS integration, QR code scanning
- **Demand Engine**: Machine learning forecasting
- **Vertical Farming**: IoT sensor integration, automation APIs
## Contributing
See [CONTRIBUTING.md](../CONTRIBUTING.md) for guidelines on contributing to LocalGreenChain.
## License
MIT License - see [LICENSE](../LICENSE) for details.

476
docs/api/transport-api.md Normal file
View file

@ -0,0 +1,476 @@
# Transport API Reference
The Transport API enables seed-to-seed tracking of all plant movement and transformation events within the LocalGreenChain ecosystem.
## Base URL
```
Development: http://localhost:3001/api/transport
Production: https://api.localgreenchain.org/transport
```
## Authentication
All transport endpoints require authentication:
```http
Authorization: Bearer <user_token>
X-Wallet-Address: <wallet_address>
```
## Endpoints
### Record Seed Acquisition
Record the acquisition of seeds from any source.
```http
POST /api/transport/seed-acquisition
Content-Type: application/json
{
"seedBatchId": "batch-2025-001",
"sourceType": "seed_bank",
"species": "Solanum lycopersicum",
"variety": "Cherokee Purple",
"quantity": 50,
"quantityUnit": "seeds",
"geneticLineageId": "lineage-tomato-cp-001",
"generation": 0,
"fromLocation": {
"latitude": 40.7128,
"longitude": -74.0060,
"locationType": "seed_bank",
"facilityName": "Baker Creek Seeds"
},
"toLocation": {
"latitude": 40.7589,
"longitude": -73.9851,
"locationType": "farm",
"facilityName": "Urban Farm NYC"
},
"transportMethod": "local_delivery",
"certifications": ["organic", "heirloom"]
}
```
**Response:**
```json
{
"success": true,
"event": {
"id": "evt-seed-acq-123",
"timestamp": "2025-01-15T10:30:00Z",
"eventType": "seed_acquisition",
"status": "verified"
},
"block": {
"index": 5,
"hash": "0000abc123...",
"carbonFootprintKg": 0.02,
"foodMiles": 5.2
}
}
```
---
### Record Planting
Record when seeds are planted.
```http
POST /api/transport/planting
Content-Type: application/json
{
"seedBatchId": "batch-2025-001",
"plantIds": ["plant-001", "plant-002", "plant-003"],
"plantingMethod": "transplant",
"quantityPlanted": 25,
"growingEnvironment": "vertical_farm",
"location": {
"latitude": 40.7589,
"longitude": -73.9851,
"locationType": "vertical_farm",
"facilityName": "Urban Farm NYC"
},
"expectedHarvestDate": "2025-03-15T00:00:00Z"
}
```
**Response:**
```json
{
"success": true,
"event": {
"id": "evt-planting-124",
"timestamp": "2025-01-20T08:00:00Z",
"eventType": "planting",
"plantIds": ["plant-001", "plant-002", "plant-003"]
},
"block": {
"index": 6,
"hash": "0000def456..."
}
}
```
---
### Record Growing Transport
Record plant movement during growing (transplanting, relocating).
```http
POST /api/transport/growing
Content-Type: application/json
{
"plantIds": ["plant-001", "plant-002"],
"reason": "transplant",
"plantStage": "seedling",
"handlingMethod": "potted",
"fromLocation": {
"latitude": 40.7589,
"longitude": -73.9851,
"locationType": "greenhouse",
"facilityName": "Seedling House A"
},
"toLocation": {
"latitude": 40.7589,
"longitude": -73.9852,
"locationType": "vertical_farm",
"facilityName": "Main Growing Tower"
},
"transportMethod": "walking",
"rootDisturbance": "minimal",
"acclimatizationRequired": true,
"acclimatizationDays": 3
}
```
---
### Record Harvest
Record harvest events with yield data.
```http
POST /api/transport/harvest
Content-Type: application/json
{
"plantIds": ["plant-001", "plant-002", "plant-003"],
"harvestBatchId": "harvest-2025-050",
"harvestType": "full",
"produceType": "tomatoes",
"grossWeight": 15.5,
"netWeight": 14.2,
"weightUnit": "kg",
"qualityGrade": "A",
"packagingType": "compostable_clamshell",
"temperatureRequired": {
"min": 10,
"max": 15,
"optimal": 12,
"unit": "celsius"
},
"shelfLifeHours": 168,
"seedsSaved": true,
"seedBatchIdCreated": "batch-2025-002"
}
```
**Response:**
```json
{
"success": true,
"event": {
"id": "evt-harvest-125",
"timestamp": "2025-03-15T06:00:00Z",
"eventType": "harvest",
"harvestBatchId": "harvest-2025-050",
"netWeight": 14.2
},
"block": {
"index": 25,
"hash": "0000ghi789...",
"carbonFootprintKg": 0.1
},
"seedBatch": {
"id": "batch-2025-002",
"parentPlants": ["plant-001", "plant-002", "plant-003"],
"generation": 1
}
}
```
---
### Record Distribution
Record movement through distribution chain.
```http
POST /api/transport/distribution
Content-Type: application/json
{
"batchIds": ["harvest-2025-050"],
"destinationType": "consumer",
"fromLocation": {
"latitude": 40.7589,
"longitude": -73.9851,
"locationType": "vertical_farm"
},
"toLocation": {
"latitude": 40.7614,
"longitude": -73.9776,
"locationType": "consumer"
},
"transportMethod": "bicycle",
"orderId": "order-2025-1234",
"customerType": "individual",
"deliveryWindow": {
"start": "2025-03-15T14:00:00Z",
"end": "2025-03-15T18:00:00Z"
}
}
```
---
### Record Seed Saving
Record seed saving from harvested plants.
```http
POST /api/transport/seed-saving
Content-Type: application/json
{
"parentPlantIds": ["plant-001", "plant-002"],
"newSeedBatchId": "batch-2025-003",
"collectionMethod": "fermentation",
"seedCount": 200,
"seedWeight": 15,
"seedWeightUnit": "grams",
"storageConditions": {
"temperature": 10,
"humidity": 40,
"lightExposure": "dark",
"containerType": "vacuum_sealed",
"desiccant": true,
"estimatedViability": 5
},
"newGenerationNumber": 2,
"availableForSharing": true,
"sharingTerms": "trade"
}
```
---
### Record Seed Sharing
Record when seeds are shared with others.
```http
POST /api/transport/seed-sharing
Content-Type: application/json
{
"seedBatchId": "batch-2025-003",
"quantityShared": 25,
"quantityUnit": "seeds",
"sharingType": "trade",
"tradeDetails": "Traded for 25 basil seeds",
"recipientId": "user-xyz-456",
"fromLocation": {...},
"toLocation": {...},
"recipientAgreement": true,
"growingCommitment": "Will grow and report back",
"reportBackRequired": true
}
```
---
## Query Endpoints
### Get Plant Journey
Get complete transport history for a plant.
```http
GET /api/transport/journey/:plantId
```
**Response:**
```json
{
"plantId": "plant-001",
"seedBatchOrigin": "batch-2025-001",
"currentCustodian": "user-abc-123",
"currentLocation": {
"latitude": 40.7614,
"longitude": -73.9776,
"locationType": "consumer"
},
"currentStage": "post_harvest",
"events": [
{
"eventType": "seed_acquisition",
"timestamp": "2025-01-15T10:30:00Z",
"fromLocation": {...},
"toLocation": {...}
},
{
"eventType": "planting",
"timestamp": "2025-01-20T08:00:00Z",
...
},
...
],
"totalFoodMiles": 12.5,
"totalCarbonKg": 0.35,
"daysInTransit": 2,
"daysGrowing": 54,
"generation": 1,
"ancestorPlantIds": ["parent-plant-001"],
"descendantSeedBatches": ["batch-2025-003"]
}
```
---
### Get Environmental Impact
Get carbon footprint and food miles for a user.
```http
GET /api/transport/footprint/:userId
```
**Response:**
```json
{
"totalCarbonKg": 15.5,
"totalFoodMiles": 250,
"carbonPerKgProduce": 0.25,
"milesPerKgProduce": 4.0,
"breakdownByMethod": {
"bicycle": { "distance": 50, "carbon": 0 },
"electric_vehicle": { "distance": 150, "carbon": 3.0 },
"walking": { "distance": 50, "carbon": 0 }
},
"breakdownByEventType": {
"seed_acquisition": { "count": 5, "carbon": 0.5 },
"distribution": { "count": 50, "carbon": 10.0 },
...
},
"comparisonToConventional": {
"carbonSaved": 140.5,
"milesSaved": 74750,
"percentageReduction": 90
}
}
```
---
### Verify Block
Verify a transport block's integrity.
```http
GET /api/transport/verify/:blockHash
```
**Response:**
```json
{
"valid": true,
"block": {
"index": 25,
"hash": "0000ghi789...",
"previousHash": "0000fgh678...",
"timestamp": "2025-03-15T06:00:00Z"
},
"chainValid": true,
"crossReferences": [
{
"userId": "user-xyz-456",
"blockIndex": 12,
"type": "receiver",
"verified": true
}
]
}
```
---
### Generate QR Code
Generate QR code data for a plant or batch.
```http
GET /api/transport/qr/:plantId
```
**Response:**
```json
{
"plantId": "plant-001",
"blockchainAddress": "0x1234567890abcdef...",
"quickLookupUrl": "https://localgreenchain.org/track/plant-001",
"lineageHash": "a1b2c3d4e5f6...",
"currentCustodian": "user-abc-123",
"lastEventType": "distribution",
"lastEventTimestamp": "2025-03-15T14:30:00Z",
"verificationCode": "A1B2C3D4",
"qrCodeImage": "data:image/png;base64,..."
}
```
---
## Error Codes
| Code | Message | Description |
|------|---------|-------------|
| 400 | Invalid request | Missing or invalid parameters |
| 401 | Unauthorized | Invalid or missing auth token |
| 403 | Forbidden | Not authorized for this resource |
| 404 | Not found | Plant/batch/block not found |
| 409 | Conflict | Duplicate event or chain conflict |
| 422 | Validation error | Data validation failed |
| 500 | Server error | Internal server error |
## Rate Limits
| Endpoint Type | Limit | Window |
|---------------|-------|--------|
| Read (GET) | 100 | per minute |
| Write (POST) | 20 | per minute |
| Batch operations | 5 | per minute |
## Webhooks
Subscribe to transport events:
```http
POST /api/transport/webhooks
Content-Type: application/json
{
"url": "https://your-server.com/webhook",
"events": ["seed_acquisition", "harvest", "distribution"],
"filters": {
"plantIds": ["plant-001", "plant-002"],
"userId": "user-abc-123"
}
}
```

View file

@ -0,0 +1,246 @@
# System Architecture Overview
LocalGreenChain is a distributed agricultural tracking platform built on blockchain technology with a focus on demand-driven, seasonal production and reduced environmental impact.
## High-Level Architecture
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ LOCALGREENCHAIN PLATFORM │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ PRESENTATION LAYER │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌────────────┐ │ │
│ │ │ Web App │ │ Mobile PWA │ │ IoT Hub │ │ QR Scanner│ │ │
│ │ │ (Next.js) │ │ (React) │ │ Dashboard │ │ App │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ └────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ API LAYER │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌────────────┐ │ │
│ │ │ Plants │ │ Transport │ │ Demand │ │ Vertical │ │ │
│ │ │ API │ │ API │ │ API │ │ Farm API │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ └────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ SERVICE LAYER │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌────────────┐ │ │
│ │ │ Blockchain │ │ Climate │ │ Demand │ │ Carbon │ │ │
│ │ │ Service │ │ Service │ │ Forecasting│ │ Calculator│ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ └────────────┘ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌────────────┐ │ │
│ │ │ Geolocation │ │ Privacy │ │ Seed │ │ Analytics │ │ │
│ │ │ Service │ │ (Tor) │ │ Service │ │ Engine │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ └────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ DATA LAYER │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ USER BLOCKCHAINS │ │ │
│ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │
│ │ │ │ User A │ │ User B │ │ User C │ │ ... │ │ │ │
│ │ │ │ Chain │ │ Chain │ │ Chain │ │ │ │ │ │
│ │ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Master │ │ Demand │ │ Climate │ │ │
│ │ │ Ledger │ │ Store │ │ Store │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
## Core Components
### 1. User Blockchain System
Each user operates their own blockchain that records:
- Plant registrations
- Growth updates
- Transport events
- Harvest records
- Seed saving activities
```typescript
// User blockchain structure
interface UserBlockchain {
userId: string;
publicKey: string;
chain: PlantBlock[];
transportChain: TransportBlock[];
demandPreferences: DemandPreference[];
}
```
### 2. Master Ledger
A global ledger that:
- Indexes all user blockchains
- Maintains cross-references for plant lineage
- Validates chain integrity
- Calculates network-wide statistics
### 3. Demand Forecasting Engine
Predicts demand based on:
- Historical consumption patterns
- Seasonal trends
- Weather forecasts
- Consumer preferences
- Regional availability
### 4. Carbon Calculator
Tracks environmental impact:
- Transport emissions
- Growing resource usage
- Packaging lifecycle
- Waste generation
## Data Flow
### Plant Registration Flow
```
User Input → Validation → Block Creation → Mining → Chain Addition → Index Update
Master Ledger Sync
```
### Transport Event Flow
```
Transport Scan → GPS Capture → Condition Check → Block Creation → Chain Update
↓ ↓ ↓
QR Decode Location Record Temp/Humidity
↓ ↓ ↓
Plant Lookup Distance Calc Alert if OOB
Carbon Calculation
```
### Demand Signal Flow
```
Consumer Preference → Aggregation → Forecasting → Grower Signals
↓ ↓
Market Data Planting Recommendations
↓ ↓
Weather API Seasonal Optimization
```
## Module Structure
```
localgreenchain/
├── lib/
│ ├── blockchain/ # Core blockchain implementation
│ │ ├── PlantBlock.ts # Block structure
│ │ ├── PlantChain.ts # Chain operations
│ │ ├── UserChain.ts # Per-user blockchain
│ │ ├── types.ts # Type definitions
│ │ └── manager.ts # Singleton manager
│ │
│ ├── transport/ # Transport tracking
│ │ ├── events.ts # Event types
│ │ ├── tracker.ts # Transport logic
│ │ ├── carbon.ts # Emissions calculator
│ │ └── qr.ts # QR code generation
│ │
│ ├── demand/ # Demand forecasting
│ │ ├── forecaster.ts # ML forecasting
│ │ ├── seasonal.ts # Seasonal planning
│ │ ├── aggregator.ts # Demand aggregation
│ │ └── signals.ts # Grower notifications
│ │
│ ├── vertical-farming/ # Vertical farm integration
│ │ ├── controller.ts # Environment control
│ │ ├── sensors.ts # IoT integration
│ │ ├── automation.ts # Automated systems
│ │ └── recipes.ts # Growing recipes
│ │
│ ├── environment/ # Environmental tracking
│ │ ├── types.ts # Environment types
│ │ └── analysis.ts # Data analysis
│ │
│ ├── privacy/ # Privacy features
│ │ └── anonymity.ts # Tor integration
│ │
│ └── services/ # External services
│ ├── geolocation.ts # Location services
│ ├── plantsnet.ts # Plants.net API
│ └── tor.ts # Tor connectivity
├── pages/
│ ├── api/ # API routes
│ │ ├── plants/ # Plant endpoints
│ │ ├── transport/ # Transport endpoints
│ │ ├── demand/ # Demand endpoints
│ │ └── vertical-farm/ # VF endpoints
│ │
│ └── [pages] # UI pages
├── components/ # React components
├── docs/ # Documentation
└── types/ # Global types
```
## Security Model
### Blockchain Security
- **Proof-of-Work**: Mining difficulty prevents rapid chain modification
- **Hash Linking**: Each block references previous hash
- **Validation**: Full chain validation before accepting data
### User Security
- **Wallet Addresses**: Pseudonymous identification
- **Tor Integration**: Optional anonymous operation
- **Location Privacy**: Configurable precision levels
### Data Integrity
- **Immutable Records**: Historical data cannot be altered
- **Cross-Verification**: Multiple parties can verify events
- **Audit Trail**: Complete history of all operations
## Scalability Strategy
### Current Implementation (10K plants)
- File-based blockchain storage
- Single-server architecture
- Client-side rendering
### Medium Scale (100K plants)
- PostgreSQL/MongoDB backend
- Redis caching layer
- Load-balanced API servers
### Large Scale (1M+ plants)
- Distributed node network
- Geographic sharding
- P2P blockchain sync
- Mobile-first architecture
## Integration Points
### External APIs
- **Plants.net**: Plant identification
- **OpenStreetMap**: Geocoding
- **Weather APIs**: Climate data
- **Carbon APIs**: Emission factors
### IoT Devices
- **Temperature sensors**: Cold chain monitoring
- **GPS trackers**: Transport tracking
- **Humidity sensors**: Environment monitoring
- **Vertical farm controllers**: Automated growing

View file

@ -0,0 +1,353 @@
# User Blockchain Architecture
Every user in LocalGreenChain operates their own blockchain, creating a decentralized network of plant lineage and transport records that collectively form a global agricultural tracking system.
## Why Per-User Blockchains?
### Traditional Centralized Approach
```
All Data → Central Database → Single Point of Failure
Trust the operator
Data can be modified
Privacy concerns
```
### LocalGreenChain Distributed Approach
```
User A Chain ←→ User B Chain ←→ User C Chain
↓ ↓ ↓
Own data Own data Own data
Own history Own history Own history
↓ ↓ ↓
Cross-referenced via Master Ledger
```
## Architecture
```
┌─────────────────────────────────────────────────────────────────────┐
│ USER BLOCKCHAIN NETWORK │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ MASTER LEDGER │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ Plant Index: plantId → [userA:block5, userB:block12] │ │ │
│ │ │ Lineage Index: parentId → [childId1, childId2...] │ │ │
│ │ │ Transport Index: batchId → [events...] │ │ │
│ │ │ User Directory: userId → publicKey, chainHash │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ ↑ │
│ ┌───────────────┼───────────────┐ │
│ ▼ ▼ ▼ │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │ USER A CHAIN │ │ USER B CHAIN │ │ USER C CHAIN │ │
│ ├───────────────┤ ├───────────────┤ ├───────────────┤ │
│ │ Block 0: Gen │ │ Block 0: Gen │ │ Block 0: Gen │ │
│ │ Block 1: Reg │ │ Block 1: Acq │ │ Block 1: Reg │ │
│ │ Block 2: Grow │ │ Block 2: Plant│ │ Block 2: Clone│ │
│ │ Block 3: Harv │ │ Block 3: Grow │ │ Block 3: Trans│ │
│ │ Block 4: Send │ │ Block 4: Recv │ │ Block 4: ... │ │
│ │ ... │ │ ... │ │ ... │ │
│ └───────────────┘ └───────────────┘ └───────────────┘ │
│ ↓ ↓ ↓ │
│ └───────────────────┼───────────────────┘ │
│ ▼ │
│ ┌───────────────────┐ │
│ │ CROSS-CHAIN REFS │ │
│ │ Plant P in A:3 │ │
│ │ received by B:4 │ │
│ │ cloned to C:2 │ │
│ └───────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
```
## Block Types
### Plant Registration Block
```typescript
interface PlantBlock {
index: number;
timestamp: string;
blockType: 'plant_registration';
plant: {
id: string;
commonName: string;
scientificName: string;
propagationType: 'original' | 'seed' | 'clone' | 'cutting';
generation: number;
parentPlantId?: string; // Cross-chain reference
location: PlantLocation;
owner: PlantOwner;
childPlants: string[];
};
previousHash: string;
hash: string;
nonce: number;
}
```
### Transport Event Block
```typescript
interface TransportBlock {
index: number;
timestamp: string;
blockType: 'transport_event';
transportEvent: {
eventType: TransportEventType;
fromLocation: TransportLocation;
toLocation: TransportLocation;
plantIds?: string[];
batchIds?: string[];
carbonFootprintKg: number;
senderId: string; // Cross-chain reference
receiverId: string; // Cross-chain reference
};
previousHash: string;
hash: string;
nonce: number;
}
```
### Environment Update Block
```typescript
interface EnvironmentBlock {
index: number;
timestamp: string;
blockType: 'environment_update';
environment: {
plantId: string;
readings: GrowingEnvironment;
healthScore: number;
alerts: EnvironmentAlert[];
};
previousHash: string;
hash: string;
nonce: number;
}
```
## Cross-Chain References
When plants move between users, cross-chain references maintain lineage:
```
User A's Chain: User B's Chain:
┌────────────────────┐ ┌────────────────────┐
│ Block 5 │ │ Block 12 │
│ Type: Send Plant │────────────→│ Type: Receive Plant│
│ PlantId: tomato-1 │ │ PlantId: tomato-1 │
│ To: UserB │ │ From: UserA │
│ ToBlock: pending │←────────────│ FromBlock: A:5 │
└────────────────────┘ └────────────────────┘
After confirmation:
Block 5.ToBlock = "B:12" // Updated with cross-reference
```
## Chain Synchronization
### Local-First Operation
```
1. User creates block locally
2. Block is mined (proof-of-work)
3. Block added to local chain
4. Sync to master ledger index
5. Cross-references updated
```
### Conflict Resolution
```
If chain fork detected:
1. Longer chain wins (most work)
2. Orphaned blocks flagged
3. Cross-references updated
4. Users notified of conflicts
```
## Privacy Model
### Default Privacy
- Location can be fuzzy (city-level)
- Owner name can be pseudonymous
- Wallet addresses are pseudonymous
### Enhanced Privacy (Tor)
- Onion routing for chain sync
- Hidden service for API access
- No IP logging
- Anonymous registration
### Public Data
- Plant species/variety
- Lineage relationships
- Transport events (anonymized)
- Environmental data (aggregated)
## Verification
### Single Chain Verification
```typescript
function verifyChain(chain: Block[]): boolean {
for (let i = 1; i < chain.length; i++) {
const current = chain[i];
const previous = chain[i - 1];
// Verify hash
if (current.hash !== calculateHash(current)) {
return false;
}
// Verify chain link
if (current.previousHash !== previous.hash) {
return false;
}
// Verify proof-of-work
if (!current.hash.startsWith('0'.repeat(difficulty))) {
return false;
}
}
return true;
}
```
### Cross-Chain Verification
```typescript
function verifyCrossReference(
senderChain: Block[],
receiverChain: Block[],
reference: CrossReference
): boolean {
const senderBlock = senderChain[reference.senderBlockIndex];
const receiverBlock = receiverChain[reference.receiverBlockIndex];
// Verify matching plant/batch IDs
if (senderBlock.plantId !== receiverBlock.plantId) {
return false;
}
// Verify timestamps (receiver after sender)
if (new Date(receiverBlock.timestamp) < new Date(senderBlock.timestamp)) {
return false;
}
// Verify signatures
if (!verifySignature(senderBlock, senderPublicKey)) {
return false;
}
return true;
}
```
## Querying the Network
### Find Plant History
```typescript
// Query across all user chains
const history = masterLedger.queryPlantHistory(plantId);
// Returns:
{
plantId: 'tomato-123',
currentOwner: 'userC',
registeredBy: 'userA',
generation: 2,
history: [
{ user: 'userA', block: 1, event: 'registered', date: '2025-01-01' },
{ user: 'userA', block: 5, event: 'cloned', date: '2025-02-15' },
{ user: 'userB', block: 12, event: 'received', date: '2025-02-16' },
{ user: 'userB', block: 20, event: 'grew', date: '2025-03-01' },
{ user: 'userC', block: 8, event: 'received', date: '2025-03-15' },
]
}
```
### Find Lineage
```typescript
const lineage = masterLedger.queryLineage(plantId, generations: 5);
// Returns tree structure
{
plant: 'tomato-123',
generation: 2,
ancestors: [
{ plant: 'tomato-orig', generation: 0, owner: 'userA' },
{ plant: 'tomato-clone1', generation: 1, owner: 'userA' }
],
descendants: [
{ plant: 'tomato-123-seed1', generation: 3, owner: 'userD' },
{ plant: 'tomato-123-seed2', generation: 3, owner: 'userE' }
],
siblings: [
{ plant: 'tomato-clone2', generation: 2, owner: 'userF' }
]
}
```
## Data Storage
### Local Storage
Each user stores their chain locally:
```
~/.localgreenchain/
├── chain.json # Main blockchain
├── transport-chain.json # Transport events
├── keys/
│ ├── private.key # User's private key
│ └── public.key # User's public key
└── cache/
└── master-index.json # Cached master ledger
```
### Cloud Sync (Optional)
```
User Chain → Encrypted → Cloud Backup
Only user can decrypt
Master ledger has index only
```
## Performance Considerations
### Block Size
- Target: < 10KB per block
- Compression for large data
- Images stored as references
### Mining Difficulty
- Adjustable per chain
- Lower for personal use (difficulty: 2)
- Higher for shared chains (difficulty: 4)
### Sync Frequency
- Real-time for active users
- Batch sync for inactive users
- Prioritize cross-references
## Security Measures
### Against Tampering
- Hash chains prevent modification
- Cross-references create witnesses
- Master ledger provides redundancy
### Against Sybil Attacks
- Proof-of-work makes spam expensive
- Reputation system for users
- Rate limiting on registrations
### Against Privacy Leaks
- Minimal data in master ledger
- Tor integration optional
- User controls visibility

View file

@ -0,0 +1,356 @@
# Demand Forecasting & Seasonal Planning
LocalGreenChain's demand forecasting system enables truly demand-driven agriculture by connecting consumer preferences with grower capabilities, optimizing for seasonal availability and minimal environmental impact.
## Core Philosophy
### Traditional Agriculture Problem
```
Grower decides → Plants → Harvests → Finds buyers → Surplus/shortage
↓ ↓ ↓ ↓ ↓
Guesses Fixed plan Hope sells Waste if not Lost income
```
### LocalGreenChain Solution
```
Consumer signals → Aggregated demand → Grower recommendation → Matched harvest
↓ ↓ ↓ ↓
Preferences Regional needs Optimal planting Pre-sold produce
```
## Demand Signal Flow
```
┌─────────────────────────────────────────────────────────────────────┐
│ DEMAND SIGNAL GENERATION │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ CONSUMER PREFERENCES │
│ ┌─────────────────┐ │
│ │ Consumer A │──┐ │
│ │ - Tomatoes: 2kg │ │ │
│ │ - Lettuce: 1kg │ │ │
│ │ - Organic only │ │ │
│ └─────────────────┘ │ │
│ ┌─────────────────┐ │ ┌─────────────────────┐ │
│ │ Consumer B │──┼─────→│ DEMAND AGGREGATOR │ │
│ │ - Tomatoes: 1kg │ │ │ │ │
│ │ - Basil: 200g │ │ │ Region: Downtown │ │
│ │ - Local pref │ │ │ Radius: 25km │ │
│ └─────────────────┘ │ │ Season: Summer │ │
│ ┌─────────────────┐ │ │ │ │
│ │ Consumer C... │──┘ │ Total Consumers: │ │
│ │ │ │ 150 │ │
│ └─────────────────┘ └──────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ DEMAND SIGNAL │ │
│ │ │ │
│ │ Tomatoes: 180kg/wk │ │
│ │ Lettuce: 95kg/wk │ │
│ │ Basil: 15kg/wk │ │
│ │ │ │
│ │ Gap: 120kg tomatoes │ │
│ │ Status: SHORTAGE │ │
│ └─────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
```
## Consumer Preferences
### Preference Structure
```typescript
interface ConsumerPreference {
// Who
consumerId: string;
location: { latitude, longitude, maxDeliveryRadiusKm };
// What they want
preferredCategories: ['leafy_greens', 'herbs', 'nightshades'];
preferredItems: [
{ produceType: 'tomato', priority: 'must_have', weeklyQuantity: 2 },
{ produceType: 'lettuce', priority: 'preferred', weeklyQuantity: 1 },
{ produceType: 'basil', priority: 'nice_to_have' }
];
// How they want it
certificationPreferences: ['organic', 'local'];
deliveryPreferences: {
method: ['home_delivery', 'farmers_market'],
frequency: 'weekly'
};
// Constraints
householdSize: 4;
weeklyBudget: 75;
}
```
### Priority Levels
| Priority | Weight | Meaning |
|----------|--------|---------|
| must_have | 10 | Essential - will seek elsewhere if not available |
| preferred | 7 | Strong preference - important for satisfaction |
| nice_to_have | 4 | Would enjoy - not essential |
| occasional | 2 | Sometimes interested - opportunistic |
## Demand Signals
### Regional Aggregation
Demand signals aggregate preferences within a geographic region:
```typescript
interface DemandSignal {
region: { centerLat, centerLon, radiusKm, name };
seasonalPeriod: 'summer';
demandItems: [
{
produceType: 'tomato',
weeklyDemandKg: 180,
consumerCount: 95,
aggregatePriority: 8.5,
urgency: 'this_week',
inSeason: true,
matchedSupply: 60,
gapKg: 120 // Opportunity for growers!
}
];
supplyStatus: 'shortage'; // surplus | balanced | shortage | critical
}
```
### Supply Matching
The system continuously matches demand with available supply:
```
Demand: 180 kg/week tomatoes
Current Supply:
- Grower A: 30 kg committed
- Grower B: 20 kg committed
- Vertical Farm X: 10 kg committed
Total: 60 kg
Gap: 120 kg/week
→ Generate planting recommendations for growers
→ Vertical farms can respond within 6-8 weeks
→ Traditional growers plan for next season
```
## Planting Recommendations
### Recommendation Engine
```typescript
// Generate recommendations for a grower
const recommendations = forecaster.generatePlantingRecommendations(
growerId,
growerLat,
growerLon,
deliveryRadiusKm: 50,
availableSpaceSqm: 1000,
season: 'summer'
);
// Returns prioritized recommendations
[
{
produceType: 'tomato',
recommendedQuantity: 200, // sqm
expectedYieldKg: 1600,
projectedDemandKg: 480, // monthly gap
projectedPricePerKg: 4.50,
projectedRevenue: 7200,
riskFactors: [...],
explanation: "Based on 3 demand signals showing 120kg weekly gap..."
},
{
produceType: 'lettuce',
recommendedQuantity: 100,
...
}
]
```
### Risk Assessment
Each recommendation includes risk factors:
| Risk Type | Description | Mitigation |
|-----------|-------------|------------|
| weather | Outdoor crop vulnerable to conditions | Consider greenhouse |
| oversupply | Other growers may fill gap | Start smaller, scale up |
| market | Demand may shift | Diversify crops |
| pest | Known pest pressure in region | Preventive measures |
## Seasonal Planning
### Seasonal Crop Calendar
```
SPRING (Mar-May)
├── Lettuce ★★★★★
├── Spinach ★★★★★
├── Peas ★★★★☆
├── Radishes ★★★★☆
└── Carrots ★★★☆☆
SUMMER (Jun-Aug)
├── Tomatoes ★★★★★
├── Peppers ★★★★★
├── Cucumbers ★★★★☆
├── Basil ★★★★★
└── Beans ★★★★☆
FALL (Sep-Nov)
├── Kale ★★★★★
├── Broccoli ★★★★☆
├── Brussels Sprouts ★★★★☆
├── Squash ★★★★☆
└── Lettuce ★★★★★
WINTER (Dec-Feb)
├── Microgreens ★★★★★ (indoor)
├── Sprouts ★★★★★ (indoor)
├── Kale ★★★★☆
└── Spinach ★★★☆☆
★ = Demand strength in season
```
### Year-Round Planning with Vertical Farms
Vertical farms can produce year-round, filling seasonal gaps:
```
Traditional Supply: ████████████░░░░░░░░░░████████████
Vertical Farm: ████████████████████████████████████
Combined: ████████████████████████████████████
Legend: █ = Supply available, ░ = Gap
```
## Forecasting Models
### Simple Moving Average
```typescript
// Basic forecast from historical data
const forecast = history.reduce((a, b) => a + b, 0) / history.length;
```
### Seasonal Adjustment
```typescript
// Apply seasonal multiplier
const seasonalFactor = isInSeason(produceType, currentSeason) ? 1.2 : 0.6;
const adjustedForecast = baseForecast * seasonalFactor;
```
### Trend Analysis
```typescript
// Detect growing/declining interest
const trend = (latestDemand - earliestDemand) / dataPoints;
const futureDemand = currentDemand + (trend * weeksAhead);
```
## Integration Points
### With Transport System
```typescript
// Transport events update demand fulfillment
transportChain.recordEvent({
eventType: 'distribution',
batchIds: ['harvest-123'],
destinationType: 'consumer',
// Updates matched supply in demand signals
});
```
### With Vertical Farms
```typescript
// Vertical farms can respond quickly to demand signals
const gaps = demandSignal.demandItems.filter(i => i.gapKg > 0 && i.urgency === 'this_week');
for (const gap of gaps) {
if (canGrowInVerticalFarm(gap.produceType)) {
// Start batch immediately
verticalFarmController.startCropBatch(...);
}
}
```
### With Blockchain
```typescript
// All demand signals and commitments recorded on chain
plantChain.recordDemandSignal(signal);
plantChain.recordSupplyCommitment(commitment);
plantChain.recordMarketMatch(match);
```
## Best Practices
### For Consumers
1. **Set Preferences Early**: The more lead time, the better matching
2. **Be Flexible**: Mark some items as "nice to have"
3. **Accept Seasonal Variety**: In-season produce is better and cheaper
4. **Provide Feedback**: Help improve recommendations
### For Growers
1. **Register Capacity**: Let the system know what you can grow
2. **Follow Recommendations**: Data-driven planting reduces risk
3. **Commit Early**: Pre-committed supply gets priority matching
4. **Diversify**: Grow multiple crops to balance risk
### For the System
1. **Aggregate Regionally**: Local matching reduces transport
2. **Prioritize Seasonal**: In-season crops have better yields
3. **Balance Supply**: Prevent oversupply situations
4. **Learn Continuously**: Improve forecasts from actual outcomes
## Environmental Impact
### Demand-Driven Benefits
| Metric | Traditional | Demand-Driven | Savings |
|--------|-------------|---------------|---------|
| Food Waste | 30-40% | 5-10% | 75% reduction |
| Transport Miles | 1,500 avg | < 50 local | 97% reduction |
| Unsold Produce | 20% | < 5% | 75% reduction |
| Energy (storage) | High | Minimal | 80% reduction |
### Carbon Calculation
```
Traditional Tomato (California to NYC):
Growing: 0.5 kg CO2/kg
Transport: 2.0 kg CO2/kg
Storage: 0.3 kg CO2/kg
Total: 2.8 kg CO2/kg
LocalGreenChain Tomato (local vertical farm):
Growing: 0.3 kg CO2/kg
Transport: 0.02 kg CO2/kg
Storage: 0.0 kg CO2/kg
Total: 0.32 kg CO2/kg
Savings: 89% carbon reduction
```

View file

@ -0,0 +1,277 @@
# Seed-to-Seed Transport System
The Seed-to-Seed Transport System is the core tracking mechanism of LocalGreenChain. It provides complete traceability from the moment a seed is sourced, through planting, growing, harvesting, consumption, and back to seed collection for the next generation.
## Overview
```
┌─────────────────────────────────────────────────────────────────────────┐
│ SEED-TO-SEED LIFECYCLE │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ SEED │───→│ TRANSIT │───→│ PLANT │───→│ GROW │ │
│ │ SOURCE │ │ TO FARM │ │ │ │ │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │ │ │
│ │ ▼ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ │ SEED │←───│ HARVEST │←───│ MATURE │ │
│ │ │ SAVE │ │ │ │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │
│ │ │ │ │
│ │ ▼ ▼ │
│ │ ┌──────────┐ ┌──────────┐ │
│ └────────→│ NEXT │ │ TRANSPORT │ │
│ │ GENERATION│ │ TO MARKET │ │
│ └──────────┘ └──────────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ CONSUMER │ │
│ │ DELIVERY │ │
│ └──────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
## Transport Event Types
### 1. Seed Acquisition Transport
Track seeds from their source to the growing location.
```typescript
interface SeedAcquisitionTransport {
eventType: 'seed_acquisition';
seedBatchId: string;
sourceType: 'seed_bank' | 'previous_harvest' | 'trade' | 'purchase' | 'wild_collected';
sourceLocation: TransportLocation;
destinationLocation: TransportLocation;
quantity: number;
unit: 'seeds' | 'grams' | 'packets';
geneticLineageId?: string;
certifications?: string[];
transportMethod: TransportMethod;
environmentalConditions: TransportEnvironment;
}
```
### 2. Growing Transport
Track movement of plants during their growing lifecycle (transplanting, relocation).
```typescript
interface GrowingTransport {
eventType: 'growing_transport';
plantId: string;
reason: 'transplant' | 'relocation' | 'hardening_off' | 'seasonal_move';
fromLocation: TransportLocation;
toLocation: TransportLocation;
plantStage: PlantStage;
handlingMethod: 'bare_root' | 'potted' | 'root_ball' | 'hydroponic_transfer';
}
```
### 3. Harvest Transport
Track movement from growing site to processing/distribution.
```typescript
interface HarvestTransport {
eventType: 'harvest_transport';
plantIds: string[];
harvestBatchId: string;
fromLocation: TransportLocation;
toLocation: TransportLocation;
harvestType: 'full_harvest' | 'partial_harvest' | 'continuous_harvest';
produceWeight: number;
unit: 'kg' | 'lbs' | 'units';
packagingType: string;
temperatureRequired: TemperatureRange;
shelfLife: number; // hours
}
```
### 4. Distribution Transport
Track movement through distribution chain to consumers.
```typescript
interface DistributionTransport {
eventType: 'distribution';
harvestBatchId: string;
fromLocation: TransportLocation;
toLocation: TransportLocation;
destinationType: 'consumer' | 'market' | 'restaurant' | 'processor' | 'vertical_farm';
transportMethod: TransportMethod;
carbonFootprint: number; // kg CO2
distance: number; // km
}
```
### 5. Seed Saving Transport
Track seed collection and storage for next generation.
```typescript
interface SeedSavingTransport {
eventType: 'seed_saving';
parentPlantIds: string[];
newSeedBatchId: string;
seedCount: number;
viabilityTestDate?: string;
germinationRate?: number;
storageLocation: TransportLocation;
storageConditions: SeedStorageConditions;
nextGenerationId: string;
}
```
## Blockchain Recording
Each transport event creates a new block in the user's blockchain:
```typescript
interface TransportBlock {
index: number;
timestamp: string;
transportEvent: TransportEvent;
previousHash: string;
hash: string;
nonce: number;
// Additional verification
signatures: {
sender?: string;
receiver?: string;
verifier?: string;
};
// Environmental impact
carbonFootprint: number;
foodMiles: number;
}
```
## Chain of Custody
The system maintains an unbroken chain of custody:
```
Seed Bank (Block #1)
↓ Transport: seed_acquisition
Grower A (Block #2)
↓ Plant & Grow
Grower A (Block #3-20) [growth updates]
↓ Transport: harvest_transport
Local Hub (Block #21)
↓ Transport: distribution
Consumer B (Block #22)
↓ Consume & Save Seeds
Seed Storage (Block #23)
↓ Transport: seed_saving → New lineage begins
```
## Environmental Impact Tracking
Every transport calculates:
1. **Carbon Footprint**
- Vehicle emissions based on transport method
- Refrigeration/climate control energy
- Packaging materials lifecycle
2. **Food Miles**
- Actual distance traveled
- Mode-weighted distance (air vs truck vs local)
3. **Time-to-Consumer**
- Freshness degradation tracking
- Optimal delivery windows
4. **Resource Usage**
- Water (for any irrigation during transport)
- Electricity (refrigeration, lighting)
- Packaging waste
## Integration with User Blockchain
Each user's blockchain maintains their personal transport history:
```typescript
class UserTransportChain {
userId: string;
chain: TransportBlock[];
// Get all plants currently in user's possession
getCurrentInventory(): PlantInventory[];
// Get complete history for a plant
getPlantJourney(plantId: string): TransportEvent[];
// Calculate total environmental impact
getTotalFootprint(): EnvironmentalImpact;
// Get lineage across generations
getMultiGenerationLineage(plantId: string, generations: number): LineageTree;
}
```
## QR Code Integration
Each plant/batch gets a unique QR code containing:
```json
{
"plantId": "lgc_abc123",
"blockchainAddress": "0x...",
"quickLookup": "https://localgreenchain.org/track/lgc_abc123",
"lineageHash": "sha256...",
"currentCustodian": "user_xyz",
"lastTransportBlock": 42
}
```
Scanning provides instant access to:
- Complete growing history
- All transport events
- Environmental conditions throughout lifecycle
- Carbon footprint calculation
- Previous generation lineage
## API Endpoints
See [Transport API Documentation](../api/transport-api.md) for complete API reference.
### Key Endpoints
```http
POST /api/transport/seed-acquisition
POST /api/transport/growing
POST /api/transport/harvest
POST /api/transport/distribution
POST /api/transport/seed-saving
GET /api/transport/journey/:plantId
GET /api/transport/footprint/:userId
GET /api/transport/verify/:blockHash
```
## Best Practices
### For Growers
1. **Record promptly**: Log transport events as they happen
2. **Verify conditions**: Note temperature, humidity, handling
3. **Save seeds systematically**: Maintain genetic diversity
4. **Minimize transport**: Source locally, deliver locally
### For Transporters
1. **Maintain cold chain**: Use temperature loggers
2. **Optimize routes**: Reduce food miles
3. **Report delays**: Update estimated arrival times
4. **Handle gently**: Protect plant integrity
### For Consumers
1. **Scan on receipt**: Verify chain of custody
2. **Report issues**: Flag any problems immediately
3. **Save seeds**: Continue the genetic lineage
4. **Share feedback**: Help improve the system

View file

@ -0,0 +1,305 @@
# Vertical Farming Integration
Vertical farming is a cornerstone of LocalGreenChain's mission to create demand-driven, seasonal agriculture that reduces the global footprint of human consumption. This section covers everything you need to know about integrating vertical farms into the LocalGreenChain ecosystem.
## Why Vertical Farming?
### Environmental Benefits
| Metric | Traditional Farming | Vertical Farming | Improvement |
|--------|-------------------|------------------|-------------|
| Water Usage | 100% | 5-10% | 90-95% reduction |
| Land Required | 100% | 1-2% | 98% reduction |
| Pesticides | Variable | Zero | 100% elimination |
| Food Miles | 1,500+ avg | < 10 miles | 99% reduction |
| Growing Season | Seasonal | Year-round | 365 days/year |
| Yield/sqm/year | 3-5 kg | 50-100 kg | 10-20x increase |
### Demand-Driven Production
Vertical farms enable true demand-driven agriculture:
```
Consumer Demand Signal → Planting Decision → Harvest → Delivery
Day 0 Day 1 Day 35 Day 35
Traditional: 6-12 months planning horizon
Vertical: 1-6 weeks from signal to delivery
```
## Architecture Overview
```
┌─────────────────────────────────────────────────────────────────────┐
│ VERTICAL FARM ECOSYSTEM │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ LOCALGREENCHAIN PLATFORM │ │
│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │
│ │ │ Demand │ │ Transport │ │ Blockchain │ │ │
│ │ │ Forecaster│ │ Tracker │ │ Ledger │ │ │
│ │ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ │ │
│ └────────┼──────────────┼──────────────┼───────────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ VERTICAL FARM CONTROLLER │ │
│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌──────────┐│ │
│ │ │Environment│ │ Nutrition │ │ Lighting │ │Automation ││ │
│ │ │ Control │ │ System │ │ System │ │ System ││ │
│ │ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ └────┬─────┘│ │
│ └────────┼──────────────┼──────────────┼─────────────┼────────┘ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ GROWING ZONES │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Zone A │ │ Zone B │ │ Zone C │ │ Zone D │ │ │
│ │ │Lettuce │ │ Basil │ │Microgrns│ │Tomatoes │ │ │
│ │ │Day 15/35│ │Day 20/42│ │Day 8/14 │ │Day 45/80│ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
```
## Growing Systems
### NFT (Nutrient Film Technique)
Best for: Leafy greens, herbs
- Thin film of nutrients flows over roots
- Low water usage
- Fast growth cycles
### DWC (Deep Water Culture)
Best for: Lettuce, basil, larger plants
- Roots suspended in aerated nutrient solution
- Simple to maintain
- Excellent oxygenation
### Aeroponics
Best for: High-value crops, root vegetables
- Roots misted with nutrients
- Maximum oxygen exposure
- Fastest growth rates
### Vertical Towers/ZipGrow
Best for: Microgreens, leafy greens, strawberries
- Space-efficient vertical growing
- Easy harvest access
- Modular expansion
## Environmental Control
### Temperature Management
```typescript
interface TemperatureControl {
// Day/night differential is crucial
dayTarget: 22; // °C
nightTarget: 18; // °C
// Acceptable ranges
minCritical: 10; // Below this: growth stops
maxCritical: 35; // Above this: heat stress
// Control systems
cooling: ['HVAC', 'evaporative', 'chilled_water'];
heating: ['heat_pump', 'radiant', 'air_handling'];
}
```
### Humidity Control
- **Seedling Stage**: 80-90% RH
- **Vegetative Stage**: 60-70% RH
- **Flowering/Fruiting**: 50-60% RH
- **Pre-Harvest**: 40-50% RH (reduces disease risk)
### CO2 Enrichment
```
Ambient CO2: ~420 ppm
Optimal Growth: 800-1200 ppm
Maximum Benefit: 1500 ppm
Warning: Above 1500 ppm provides diminishing returns
and increases operational costs
```
### Lighting
| Stage | PPFD (µmol/m²/s) | DLI (mol/m²/day) | Hours |
|-------|-----------------|------------------|-------|
| Germination | 100-150 | 4-6 | 16-18 |
| Seedling | 150-250 | 8-12 | 18 |
| Vegetative | 250-400 | 15-25 | 16-18 |
| Flowering | 400-600 | 25-40 | 12-16 |
## Growing Recipes
LocalGreenChain includes pre-configured growing recipes for common crops:
### Lettuce (35-day cycle)
```yaml
Recipe: Butterhead Lettuce - Fast Cycle
Stages:
- Germination (Day 0-3): 150 PPFD, 80% RH, EC 0.8
- Seedling (Day 4-10): 200 PPFD, 70% RH, EC 1.2
- Vegetative (Day 11-28): 300 PPFD, 65% RH, EC 1.6
- Finishing (Day 29-35): 250 PPFD, 60% RH, EC 1.2
Expected Yield: 180g per head
Yield/sqm/year: 4000g (11+ cycles)
```
### Basil (42-day cycle)
```yaml
Recipe: Genovese Basil - Aromatic
Stages:
- Germination (Day 0-5): 100 PPFD, 80% RH, EC 0.6
- Seedling (Day 6-14): 200 PPFD, 70% RH, EC 1.0
- Vegetative (Day 15-35): 400 PPFD, 65% RH, EC 1.4
- Harvest Ready (Day 36-42): 350 PPFD, 60% RH, EC 1.0
Expected Yield: 120g per plant
Yield/sqm/year: 2400g (8+ cycles)
```
### Microgreens (14-day cycle)
```yaml
Recipe: Microgreens Mix - Quick Turn
Stages:
- Sowing (Day 0-2): Dark, 90% RH
- Germination (Day 3-5): 100 PPFD, 80% RH
- Growth (Day 6-12): 250 PPFD, 65% RH, EC 0.8
- Harvest (Day 13-14): 200 PPFD, 60% RH
Expected Yield: 200g per tray
Yield/sqm/year: 2000g (26 cycles)
```
## Integration with LocalGreenChain
### Demand-Driven Planting
```typescript
// Example: Demand signal triggers planting
const demandSignal = demandForecaster.generateDemandSignal(
lat, lon, radiusKm, regionName, season
);
// Find gaps in supply
const gaps = demandSignal.demandItems.filter(item => item.gapKg > 0);
// Start crop batches to fill gaps
for (const gap of gaps) {
const recipe = findBestRecipe(gap.produceType);
const batch = verticalFarmController.startCropBatch(
farmId,
availableZoneId,
recipe.id,
seedBatchId,
calculatePlantCount(gap.gapKg, recipe.expectedYieldGrams)
);
}
```
### Blockchain Recording
Every crop batch is recorded on the blockchain:
1. **Planting Event**: Seed batch origin, recipe used, zone assigned
2. **Environment Logs**: Daily readings stored for traceability
3. **Harvest Event**: Actual yield, quality grade, destination
4. **Transport**: Movement from farm to consumer
### Consumer Traceability
Consumers can scan a QR code to see:
- Which vertical farm grew their produce
- The exact growing recipe used
- All environmental conditions during growth
- Seed lineage and generation
- Carbon footprint of production
## Best Practices
### For Operators
1. **Start Small**: Begin with easy crops (lettuce, microgreens)
2. **Standardize Recipes**: Use proven recipes before experimenting
3. **Monitor Closely**: Environmental data is your best friend
4. **Maintain Equipment**: Preventive maintenance prevents crop loss
5. **Track Everything**: Data enables optimization
### For Integration
1. **Register Farm**: Add your facility to LocalGreenChain
2. **Configure Zones**: Set up growing areas with sensor integration
3. **Import Recipes**: Use community recipes or create custom ones
4. **Connect Demand**: Link to regional demand signals
5. **Record Everything**: Let the blockchain track your success
## API Reference
See [Vertical Farming API](../api/vertical-farming-api.md) for complete API documentation.
### Key Endpoints
```http
POST /api/vertical-farm/register
POST /api/vertical-farm/zone/create
POST /api/vertical-farm/batch/start
PUT /api/vertical-farm/batch/:id/environment
POST /api/vertical-farm/batch/:id/harvest
GET /api/vertical-farm/:id/analytics
GET /api/vertical-farm/recipes
```
## Resource Efficiency
### Energy Optimization
- **LED Lighting**: 2.5-3.0 µmol/J efficacy
- **Climate Control**: Heat pump systems
- **Renewable Integration**: Solar/wind compatible
- **Off-Peak Operation**: Shift energy-intensive tasks
### Water Conservation
- **Recirculating Systems**: 95%+ water reuse
- **Condensate Recovery**: Capture HVAC moisture
- **Rainwater Harvesting**: Supplement fresh water
- **Quality Monitoring**: Prevent nutrient lockout
## Scaling Considerations
### Small Scale (< 1,000 sqm)
- Single facility management
- Manual/semi-automated
- 5-10 crop varieties
- Local market focus
### Medium Scale (1,000-10,000 sqm)
- Multi-zone automation
- Recipe optimization
- 20-30 crop varieties
- Regional distribution
### Large Scale (> 10,000 sqm)
- Full automation
- AI-driven optimization
- 50+ crop varieties
- Multi-region supply
- Integration with multiple demand centers
## Future Development
- **AI Optimization**: Machine learning for recipe improvement
- **Robotic Harvesting**: Fully automated crop handling
- **Genetic Tracking**: DNA verification of plant lineage
- **Carbon Credits**: Earn credits for sustainable production
- **Community Recipes**: Share and rate growing recipes

578
lib/demand/forecaster.ts Normal file
View file

@ -0,0 +1,578 @@
/**
* Demand Forecaster for LocalGreenChain
* Aggregates consumer preferences and generates planting recommendations
*/
import {
ConsumerPreference,
DemandSignal,
DemandItem,
PlantingRecommendation,
DemandForecast,
ProduceForecast,
SupplyCommitment,
MarketMatch,
SeasonalPlan,
ProduceCategory,
RiskFactor
} from './types';
// Seasonal growing data for common produce
const SEASONAL_DATA: Record<string, {
categories: ProduceCategory[];
growingDays: number;
seasons: ('spring' | 'summer' | 'fall' | 'winter')[];
yieldPerSqm: number; // kg per sq meter
idealTemp: { min: number; max: number };
}> = {
'lettuce': {
categories: ['leafy_greens'],
growingDays: 45,
seasons: ['spring', 'fall'],
yieldPerSqm: 4,
idealTemp: { min: 10, max: 21 }
},
'tomato': {
categories: ['nightshades', 'fruits'],
growingDays: 80,
seasons: ['summer'],
yieldPerSqm: 8,
idealTemp: { min: 18, max: 29 }
},
'spinach': {
categories: ['leafy_greens'],
growingDays: 40,
seasons: ['spring', 'fall', 'winter'],
yieldPerSqm: 3,
idealTemp: { min: 5, max: 18 }
},
'kale': {
categories: ['leafy_greens', 'brassicas'],
growingDays: 55,
seasons: ['spring', 'fall', 'winter'],
yieldPerSqm: 3.5,
idealTemp: { min: 5, max: 24 }
},
'basil': {
categories: ['herbs'],
growingDays: 30,
seasons: ['spring', 'summer'],
yieldPerSqm: 2,
idealTemp: { min: 18, max: 29 }
},
'microgreens': {
categories: ['microgreens'],
growingDays: 14,
seasons: ['spring', 'summer', 'fall', 'winter'],
yieldPerSqm: 1.5,
idealTemp: { min: 18, max: 24 }
},
'cucumber': {
categories: ['squash'],
growingDays: 60,
seasons: ['summer'],
yieldPerSqm: 10,
idealTemp: { min: 18, max: 30 }
},
'pepper': {
categories: ['nightshades'],
growingDays: 75,
seasons: ['summer'],
yieldPerSqm: 6,
idealTemp: { min: 18, max: 29 }
},
'carrot': {
categories: ['root_vegetables'],
growingDays: 70,
seasons: ['spring', 'fall'],
yieldPerSqm: 5,
idealTemp: { min: 7, max: 24 }
},
'strawberry': {
categories: ['berries', 'fruits'],
growingDays: 90,
seasons: ['spring', 'summer'],
yieldPerSqm: 3,
idealTemp: { min: 15, max: 26 }
}
};
export class DemandForecaster {
private preferences: Map<string, ConsumerPreference> = new Map();
private supplyCommitments: Map<string, SupplyCommitment> = new Map();
private demandSignals: Map<string, DemandSignal> = new Map();
private marketMatches: Map<string, MarketMatch> = new Map();
/**
* Register consumer preference
*/
registerPreference(preference: ConsumerPreference): void {
this.preferences.set(preference.consumerId, preference);
}
/**
* Register supply commitment from grower
*/
registerSupply(commitment: SupplyCommitment): void {
this.supplyCommitments.set(commitment.id, commitment);
}
/**
* Generate demand signal for a region
*/
generateDemandSignal(
centerLat: number,
centerLon: number,
radiusKm: number,
regionName: string,
season: 'spring' | 'summer' | 'fall' | 'winter'
): DemandSignal {
// Find consumers in region
const regionalConsumers = Array.from(this.preferences.values()).filter(pref => {
const distance = this.calculateDistance(
centerLat, centerLon,
pref.location.latitude, pref.location.longitude
);
return distance <= radiusKm;
});
// Aggregate demand by produce type
const demandMap = new Map<string, {
consumers: Set<string>;
totalWeeklyKg: number;
priorities: number[];
certifications: Set<string>;
prices: number[];
}>();
for (const consumer of regionalConsumers) {
for (const item of consumer.preferredItems) {
const existing = demandMap.get(item.produceType) || {
consumers: new Set(),
totalWeeklyKg: 0,
priorities: [],
certifications: new Set(),
prices: []
};
existing.consumers.add(consumer.consumerId);
// Calculate weekly demand based on household size
const weeklyKg = (item.weeklyQuantity || 0.5) * consumer.householdSize;
existing.totalWeeklyKg += weeklyKg;
// Track priority
const priorityValue = item.priority === 'must_have' ? 10 :
item.priority === 'preferred' ? 7 :
item.priority === 'nice_to_have' ? 4 : 2;
existing.priorities.push(priorityValue);
// Track certifications
consumer.certificationPreferences.forEach(cert =>
existing.certifications.add(cert)
);
// Track price expectations
if (consumer.weeklyBudget && consumer.preferredItems.length > 0) {
const avgPricePerItem = consumer.weeklyBudget / consumer.preferredItems.length;
existing.prices.push(avgPricePerItem / weeklyKg);
}
demandMap.set(item.produceType, existing);
}
}
// Convert to demand items
const demandItems: DemandItem[] = Array.from(demandMap.entries()).map(([produceType, data]) => {
const seasonalData = SEASONAL_DATA[produceType.toLowerCase()];
const inSeason = seasonalData?.seasons.includes(season) ?? true;
const avgPriority = data.priorities.length > 0
? data.priorities.reduce((a, b) => a + b, 0) / data.priorities.length
: 5;
const avgPrice = data.prices.length > 0
? data.prices.reduce((a, b) => a + b, 0) / data.prices.length
: 5;
return {
produceType,
category: seasonalData?.categories[0] || 'leafy_greens',
weeklyDemandKg: data.totalWeeklyKg,
monthlyDemandKg: data.totalWeeklyKg * 4,
consumerCount: data.consumers.size,
aggregatePriority: Math.round(avgPriority),
urgency: avgPriority >= 8 ? 'immediate' :
avgPriority >= 6 ? 'this_week' :
avgPriority >= 4 ? 'this_month' : 'next_season',
preferredCertifications: Array.from(data.certifications),
averageWillingPrice: Math.round(avgPrice * 100) / 100,
priceUnit: 'per_kg',
inSeason,
seasonalAvailability: {
spring: seasonalData?.seasons.includes('spring') ?? true,
summer: seasonalData?.seasons.includes('summer') ?? true,
fall: seasonalData?.seasons.includes('fall') ?? true,
winter: seasonalData?.seasons.includes('winter') ?? false
},
matchedSupply: 0,
matchedGrowers: 0,
gapKg: data.totalWeeklyKg
};
});
// Calculate supply matching
const regionalSupply = Array.from(this.supplyCommitments.values()).filter(supply =>
supply.status === 'available' || supply.status === 'partially_committed'
);
for (const item of demandItems) {
const matchingSupply = regionalSupply.filter(s =>
s.produceType.toLowerCase() === item.produceType.toLowerCase()
);
item.matchedSupply = matchingSupply.reduce((sum, s) => sum + s.remainingKg, 0);
item.matchedGrowers = matchingSupply.length;
item.gapKg = Math.max(0, item.weeklyDemandKg - item.matchedSupply);
}
const totalWeeklyDemand = demandItems.reduce((sum, item) => sum + item.weeklyDemandKg, 0);
const totalSupply = demandItems.reduce((sum, item) => sum + item.matchedSupply, 0);
const totalGap = demandItems.reduce((sum, item) => sum + item.gapKg, 0);
const signal: DemandSignal = {
id: `demand-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
timestamp: new Date().toISOString(),
region: {
centerLat,
centerLon,
radiusKm,
name: regionName
},
periodStart: new Date().toISOString(),
periodEnd: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
seasonalPeriod: season,
demandItems: demandItems.sort((a, b) => b.aggregatePriority - a.aggregatePriority),
totalConsumers: regionalConsumers.length,
totalWeeklyDemandKg: totalWeeklyDemand,
confidenceLevel: Math.min(100, regionalConsumers.length * 2),
currentSupplyKg: totalSupply,
supplyGapKg: totalGap,
supplyStatus: totalGap <= 0 ? 'surplus' :
totalGap < totalWeeklyDemand * 0.1 ? 'balanced' :
totalGap < totalWeeklyDemand * 0.3 ? 'shortage' : 'critical'
};
this.demandSignals.set(signal.id, signal);
return signal;
}
/**
* Generate planting recommendations for a grower
*/
generatePlantingRecommendations(
growerId: string,
growerLat: number,
growerLon: number,
deliveryRadiusKm: number,
availableSpaceSqm: number,
season: 'spring' | 'summer' | 'fall' | 'winter'
): PlantingRecommendation[] {
const recommendations: PlantingRecommendation[] = [];
// Find relevant demand signals
const relevantSignals = Array.from(this.demandSignals.values()).filter(signal => {
const distance = this.calculateDistance(
growerLat, growerLon,
signal.region.centerLat, signal.region.centerLon
);
return distance <= deliveryRadiusKm + signal.region.radiusKm &&
signal.seasonalPeriod === season;
});
// Aggregate demand items across signals
const aggregatedDemand = new Map<string, {
totalGapKg: number;
avgPrice: number;
avgPriority: number;
signalIds: string[];
}>();
for (const signal of relevantSignals) {
for (const item of signal.demandItems) {
if (item.gapKg > 0 && item.inSeason) {
const existing = aggregatedDemand.get(item.produceType) || {
totalGapKg: 0,
avgPrice: 0,
avgPriority: 0,
signalIds: []
};
existing.totalGapKg += item.gapKg;
existing.avgPrice = (existing.avgPrice * existing.signalIds.length + item.averageWillingPrice) /
(existing.signalIds.length + 1);
existing.avgPriority = (existing.avgPriority * existing.signalIds.length + item.aggregatePriority) /
(existing.signalIds.length + 1);
existing.signalIds.push(signal.id);
aggregatedDemand.set(item.produceType, existing);
}
}
}
// Sort by opportunity score (gap * price * priority)
const sortedOpportunities = Array.from(aggregatedDemand.entries())
.map(([produceType, data]) => ({
produceType,
...data,
score: data.totalGapKg * data.avgPrice * data.avgPriority / 100
}))
.sort((a, b) => b.score - a.score);
// Allocate space to top opportunities
let remainingSpace = availableSpaceSqm;
for (const opportunity of sortedOpportunities) {
if (remainingSpace <= 0) break;
const seasonalData = SEASONAL_DATA[opportunity.produceType.toLowerCase()];
if (!seasonalData) continue;
// Calculate space needed
const yieldPerSqm = seasonalData.yieldPerSqm;
const neededSpace = Math.min(
remainingSpace,
opportunity.totalGapKg / yieldPerSqm
);
if (neededSpace < 1) continue;
const expectedYield = neededSpace * yieldPerSqm;
const projectedRevenue = expectedYield * opportunity.avgPrice;
// Assess risks
const riskFactors: RiskFactor[] = [];
if (seasonalData.seasons.length === 1) {
riskFactors.push({
type: 'weather',
severity: 'medium',
description: 'Single season crop with weather sensitivity',
mitigationSuggestion: 'Consider greenhouse/vertical farm growing'
});
}
if (opportunity.totalGapKg > expectedYield * 3) {
riskFactors.push({
type: 'market',
severity: 'low',
description: 'Strong demand exceeds your capacity',
mitigationSuggestion: 'Consider partnering with other growers'
});
}
if (opportunity.totalGapKg < expectedYield * 0.5) {
riskFactors.push({
type: 'oversupply',
severity: 'medium',
description: 'Risk of oversupply if demand doesn\'t grow',
mitigationSuggestion: 'Start with smaller quantity and scale up'
});
}
const overallRisk = riskFactors.some(r => r.severity === 'high') ? 'high' :
riskFactors.some(r => r.severity === 'medium') ? 'medium' : 'low';
const plantByDate = new Date();
const harvestStart = new Date(plantByDate.getTime() + seasonalData.growingDays * 24 * 60 * 60 * 1000);
const harvestEnd = new Date(harvestStart.getTime() + 21 * 24 * 60 * 60 * 1000);
recommendations.push({
id: `rec-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
timestamp: new Date().toISOString(),
growerId,
produceType: opportunity.produceType,
category: seasonalData.categories[0],
recommendedQuantity: Math.round(neededSpace),
quantityUnit: 'sqm',
expectedYieldKg: Math.round(expectedYield * 10) / 10,
yieldConfidence: 75,
plantByDate: plantByDate.toISOString(),
expectedHarvestStart: harvestStart.toISOString(),
expectedHarvestEnd: harvestEnd.toISOString(),
growingDays: seasonalData.growingDays,
projectedDemandKg: opportunity.totalGapKg,
projectedPricePerKg: Math.round(opportunity.avgPrice * 100) / 100,
projectedRevenue: Math.round(projectedRevenue * 100) / 100,
marketConfidence: Math.min(90, 50 + opportunity.signalIds.length * 10),
riskFactors,
overallRisk,
demandSignalIds: opportunity.signalIds,
explanation: `Based on ${opportunity.signalIds.length} demand signal(s) showing a gap of ${Math.round(opportunity.totalGapKg)}kg ` +
`for ${opportunity.produceType}. With ${Math.round(neededSpace)} sqm, you can produce approximately ${Math.round(expectedYield)}kg ` +
`at an expected price of $${opportunity.avgPrice.toFixed(2)}/kg.`
});
remainingSpace -= neededSpace;
}
return recommendations;
}
/**
* Generate demand forecast
*/
generateForecast(
regionName: string,
forecastWeeks: number = 12
): DemandForecast {
const forecasts: ProduceForecast[] = [];
// Get historical demand signals for the region
const historicalSignals = Array.from(this.demandSignals.values())
.filter(s => s.region.name === regionName)
.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
// Aggregate by produce type
const produceHistory = new Map<string, number[]>();
for (const signal of historicalSignals) {
for (const item of signal.demandItems) {
const history = produceHistory.get(item.produceType) || [];
history.push(item.weeklyDemandKg);
produceHistory.set(item.produceType, history);
}
}
// Generate forecasts
for (const [produceType, history] of produceHistory) {
if (history.length === 0) continue;
const avgDemand = history.reduce((a, b) => a + b, 0) / history.length;
const trend = history.length > 1
? (history[history.length - 1] - history[0]) / history.length
: 0;
const seasonalData = SEASONAL_DATA[produceType.toLowerCase()];
const currentSeason = this.getCurrentSeason();
const seasonalFactor = seasonalData?.seasons.includes(currentSeason) ? 1.2 : 0.6;
const predictedDemand = (avgDemand + trend * forecastWeeks) * seasonalFactor;
forecasts.push({
produceType,
category: seasonalData?.categories[0] || 'leafy_greens',
predictedDemandKg: Math.round(predictedDemand * 10) / 10,
confidenceInterval: {
low: Math.round(predictedDemand * 0.7 * 10) / 10,
high: Math.round(predictedDemand * 1.3 * 10) / 10
},
confidence: Math.min(95, 50 + history.length * 5),
trend: trend > 0.1 ? 'increasing' : trend < -0.1 ? 'decreasing' : 'stable',
trendStrength: Math.min(100, Math.abs(trend) * 100),
seasonalFactor,
predictedPricePerKg: 5, // Default price
priceConfidenceInterval: { low: 3, high: 8 },
factors: [
{
name: 'Seasonal adjustment',
type: 'seasonal',
impact: Math.round((seasonalFactor - 1) * 100),
description: seasonalData?.seasons.includes(currentSeason)
? 'In season - higher demand expected'
: 'Out of season - lower demand expected'
},
{
name: 'Historical trend',
type: 'trend',
impact: Math.round(trend * 10),
description: trend > 0 ? 'Growing popularity' : trend < 0 ? 'Declining interest' : 'Stable demand'
}
]
});
}
return {
id: `forecast-${Date.now()}`,
generatedAt: new Date().toISOString(),
region: regionName,
forecastPeriod: {
start: new Date().toISOString(),
end: new Date(Date.now() + forecastWeeks * 7 * 24 * 60 * 60 * 1000).toISOString()
},
forecasts: forecasts.sort((a, b) => b.predictedDemandKg - a.predictedDemandKg),
modelVersion: '1.0.0',
dataPointsUsed: historicalSignals.length,
lastTrainingDate: new Date().toISOString()
};
}
private getCurrentSeason(): 'spring' | 'summer' | 'fall' | 'winter' {
const month = new Date().getMonth();
if (month >= 2 && month <= 4) return 'spring';
if (month >= 5 && month <= 7) return 'summer';
if (month >= 8 && month <= 10) return 'fall';
return 'winter';
}
private calculateDistance(lat1: number, lon1: number, lat2: number, lon2: number): number {
const R = 6371;
const dLat = (lat2 - lat1) * Math.PI / 180;
const dLon = (lon2 - lon1) * Math.PI / 180;
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c;
}
/**
* Export state
*/
toJSON(): object {
return {
preferences: Array.from(this.preferences.entries()),
supplyCommitments: Array.from(this.supplyCommitments.entries()),
demandSignals: Array.from(this.demandSignals.entries()),
marketMatches: Array.from(this.marketMatches.entries())
};
}
/**
* Import state
*/
static fromJSON(data: any): DemandForecaster {
const forecaster = new DemandForecaster();
if (data.preferences) {
for (const [key, value] of data.preferences) {
forecaster.preferences.set(key, value);
}
}
if (data.supplyCommitments) {
for (const [key, value] of data.supplyCommitments) {
forecaster.supplyCommitments.set(key, value);
}
}
if (data.demandSignals) {
for (const [key, value] of data.demandSignals) {
forecaster.demandSignals.set(key, value);
}
}
if (data.marketMatches) {
for (const [key, value] of data.marketMatches) {
forecaster.marketMatches.set(key, value);
}
}
return forecaster;
}
}
// Singleton instance
let forecasterInstance: DemandForecaster | null = null;
export function getDemandForecaster(): DemandForecaster {
if (!forecasterInstance) {
forecasterInstance = new DemandForecaster();
}
return forecasterInstance;
}

379
lib/demand/types.ts Normal file
View file

@ -0,0 +1,379 @@
/**
* Demand Forecasting Types for LocalGreenChain
* Enables demand-driven, seasonal agriculture planning
*/
// Consumer preference for produce
export interface ConsumerPreference {
consumerId: string;
createdAt: string;
updatedAt: string;
// Location for local matching
location: {
latitude: number;
longitude: number;
maxDeliveryRadiusKm: number;
city?: string;
region?: string;
};
// Dietary preferences
dietaryType: ('omnivore' | 'vegetarian' | 'vegan' | 'pescatarian' | 'flexitarian')[];
allergies: string[];
dislikes: string[];
// Produce preferences
preferredCategories: ProduceCategory[];
preferredItems: ProducePreference[];
// Quality preferences
certificationPreferences: ('organic' | 'non_gmo' | 'biodynamic' | 'local' | 'heirloom')[];
freshnessImportance: 1 | 2 | 3 | 4 | 5;
priceImportance: 1 | 2 | 3 | 4 | 5;
sustainabilityImportance: 1 | 2 | 3 | 4 | 5;
// Delivery preferences
deliveryPreferences: {
method: ('home_delivery' | 'pickup_point' | 'farmers_market' | 'csa')[];
frequency: 'daily' | 'twice_weekly' | 'weekly' | 'bi_weekly' | 'monthly';
preferredDays: ('monday' | 'tuesday' | 'wednesday' | 'thursday' | 'friday' | 'saturday' | 'sunday')[];
};
// Household info
householdSize: number;
weeklyBudget?: number;
currency?: string;
}
export type ProduceCategory =
| 'leafy_greens'
| 'root_vegetables'
| 'nightshades'
| 'brassicas'
| 'alliums'
| 'legumes'
| 'squash'
| 'herbs'
| 'microgreens'
| 'sprouts'
| 'mushrooms'
| 'fruits'
| 'berries'
| 'citrus'
| 'tree_fruits'
| 'melons'
| 'edible_flowers';
export interface ProducePreference {
produceType: string;
category: ProduceCategory;
priority: 'must_have' | 'preferred' | 'nice_to_have' | 'occasional';
weeklyQuantity?: number;
unit?: string;
varietyPreferences?: string[];
seasonalOnly: boolean;
}
// Demand signal from aggregated preferences
export interface DemandSignal {
id: string;
timestamp: string;
// Location scope
region: {
centerLat: number;
centerLon: number;
radiusKm: number;
name: string;
};
// Time scope
periodStart: string;
periodEnd: string;
seasonalPeriod: 'spring' | 'summer' | 'fall' | 'winter';
// Aggregated demand
demandItems: DemandItem[];
// Statistics
totalConsumers: number;
totalWeeklyDemandKg: number;
confidenceLevel: number; // 0-100
// Supply status
currentSupplyKg: number;
supplyGapKg: number;
supplyStatus: 'surplus' | 'balanced' | 'shortage' | 'critical';
}
export interface DemandItem {
produceType: string;
category: ProduceCategory;
scientificName?: string;
// Quantities
weeklyDemandKg: number;
monthlyDemandKg: number;
consumerCount: number;
// Priority
aggregatePriority: number; // 1-10
urgency: 'immediate' | 'this_week' | 'this_month' | 'next_season';
// Preferences
preferredCertifications: string[];
averageWillingPrice: number;
priceUnit: string;
// Seasonal info
inSeason: boolean;
seasonalAvailability: {
spring: boolean;
summer: boolean;
fall: boolean;
winter: boolean;
};
// Supply matching
matchedSupply: number;
matchedGrowers: number;
gapKg: number;
}
// Grower planting recommendation
export interface PlantingRecommendation {
id: string;
timestamp: string;
growerId: string;
// Recommendation details
produceType: string;
variety?: string;
category: ProduceCategory;
// Quantities
recommendedQuantity: number;
quantityUnit: 'plants' | 'seeds' | 'kg_expected_yield';
expectedYieldKg: number;
yieldConfidence: number; // 0-100
// Timing
plantByDate: string;
expectedHarvestStart: string;
expectedHarvestEnd: string;
growingDays: number;
// Market opportunity
projectedDemandKg: number;
projectedPricePerKg: number;
projectedRevenue: number;
marketConfidence: number; // 0-100
// Risk assessment
riskFactors: RiskFactor[];
overallRisk: 'low' | 'medium' | 'high';
// Reasoning
demandSignalIds: string[];
explanation: string;
}
export interface RiskFactor {
type: 'weather' | 'pest' | 'disease' | 'market' | 'oversupply' | 'labor' | 'transport';
severity: 'low' | 'medium' | 'high';
description: string;
mitigationSuggestion?: string;
}
// Seasonal planning
export interface SeasonalPlan {
id: string;
growerId: string;
year: number;
season: 'spring' | 'summer' | 'fall' | 'winter';
// Location context
location: {
latitude: number;
longitude: number;
hardinessZone: string;
microclimate?: string;
};
// Growing capacity
growingCapacity: {
outdoorSqMeters?: number;
greenhouseSqMeters?: number;
verticalFarmSqMeters?: number;
hydroponicUnits?: number;
};
// Planned crops
plannedCrops: PlannedCrop[];
// Expected outcomes
expectedTotalYieldKg: number;
expectedRevenue: number;
expectedCarbonFootprintKg: number;
// Status
status: 'draft' | 'confirmed' | 'in_progress' | 'completed';
completionPercentage?: number;
}
export interface PlannedCrop {
produceType: string;
variety?: string;
// Planting
plantingDate: string;
plantingMethod: 'direct_sow' | 'transplant' | 'hydroponic' | 'aeroponic';
quantity: number;
quantityUnit: 'plants' | 'seeds' | 'sqm' | 'units';
// Space allocation
allocatedSpace: number;
spaceUnit: 'sqm' | 'sqft' | 'units';
growingLocation: 'outdoor' | 'greenhouse' | 'vertical_farm' | 'indoor';
// Expected harvest
expectedHarvestStart: string;
expectedHarvestEnd: string;
expectedYieldKg: number;
// Market allocation
preCommittedKg: number;
preCommittedTo: string[]; // consumer IDs or market names
// Status
status: 'planned' | 'planted' | 'growing' | 'harvesting' | 'completed' | 'failed';
actualYieldKg?: number;
}
// Demand forecast model
export interface DemandForecast {
id: string;
generatedAt: string;
// Scope
region: string;
forecastPeriod: {
start: string;
end: string;
};
// Forecasts by produce type
forecasts: ProduceForecast[];
// Model info
modelVersion: string;
dataPointsUsed: number;
lastTrainingDate: string;
}
export interface ProduceForecast {
produceType: string;
category: ProduceCategory;
// Predicted demand
predictedDemandKg: number;
confidenceInterval: {
low: number;
high: number;
};
confidence: number; // 0-100
// Trends
trend: 'increasing' | 'stable' | 'decreasing';
trendStrength: number; // 0-100
seasonalFactor: number; // multiplier
// Price prediction
predictedPricePerKg: number;
priceConfidenceInterval: {
low: number;
high: number;
};
// Influencing factors
factors: ForecastFactor[];
}
export interface ForecastFactor {
name: string;
type: 'seasonal' | 'trend' | 'event' | 'weather' | 'economic' | 'preference';
impact: number; // -100 to +100
description: string;
}
// Supply commitment from grower
export interface SupplyCommitment {
id: string;
growerId: string;
timestamp: string;
produceType: string;
variety?: string;
// Commitment details
committedQuantityKg: number;
availableFrom: string;
availableUntil: string;
// Pricing
pricePerKg: number;
currency: string;
minimumOrderKg: number;
bulkDiscountThreshold?: number;
bulkDiscountPercent?: number;
// Quality
certifications: string[];
freshnessGuaranteeHours: number;
// Delivery
deliveryRadiusKm: number;
deliveryMethods: ('grower_delivery' | 'customer_pickup' | 'hub_dropoff' | 'third_party')[];
// Status
status: 'available' | 'partially_committed' | 'fully_committed' | 'expired';
remainingKg: number;
}
// Market match between supply and demand
export interface MarketMatch {
id: string;
timestamp: string;
demandSignalId: string;
supplyCommitmentId: string;
consumerId: string;
growerId: string;
produceType: string;
matchedQuantityKg: number;
// Transaction details
agreedPricePerKg: number;
totalPrice: number;
currency: string;
// Delivery
deliveryDate: string;
deliveryMethod: string;
deliveryLocation: {
latitude: number;
longitude: number;
address?: string;
};
// Status
status: 'pending' | 'confirmed' | 'in_transit' | 'delivered' | 'completed' | 'cancelled';
// Ratings
consumerRating?: number;
growerRating?: number;
feedback?: string;
}

516
lib/transport/tracker.ts Normal file
View file

@ -0,0 +1,516 @@
/**
* Transport Tracker for LocalGreenChain
* Manages seed-to-seed transport events and blockchain recording
*/
import crypto from 'crypto';
import {
TransportEvent,
TransportBlock,
TransportLocation,
TransportMethod,
PlantJourney,
EnvironmentalImpact,
TransportQRData,
CARBON_FACTORS,
SeedAcquisitionEvent,
PlantingEvent,
GrowingTransportEvent,
HarvestEvent,
ProcessingEvent,
DistributionEvent,
ConsumerDeliveryEvent,
SeedSavingEvent,
SeedSharingEvent,
TransportEventType
} from './types';
/**
* TransportChain - Blockchain for transport events
*/
export class TransportChain {
public chain: TransportBlock[];
public difficulty: number;
private eventIndex: Map<string, TransportBlock[]>;
private plantEvents: Map<string, TransportEvent[]>;
private batchEvents: Map<string, TransportEvent[]>;
constructor(difficulty: number = 3) {
this.chain = [this.createGenesisBlock()];
this.difficulty = difficulty;
this.eventIndex = new Map();
this.plantEvents = new Map();
this.batchEvents = new Map();
}
private createGenesisBlock(): TransportBlock {
const genesisEvent: SeedAcquisitionEvent = {
id: 'genesis-transport-0',
timestamp: new Date().toISOString(),
eventType: 'seed_acquisition',
fromLocation: {
latitude: 0,
longitude: 0,
locationType: 'seed_bank',
facilityName: 'LocalGreenChain Genesis'
},
toLocation: {
latitude: 0,
longitude: 0,
locationType: 'seed_bank',
facilityName: 'LocalGreenChain Genesis'
},
distanceKm: 0,
durationMinutes: 0,
transportMethod: 'walking',
carbonFootprintKg: 0,
senderId: 'system',
receiverId: 'system',
status: 'verified',
seedBatchId: 'genesis-seed-batch',
sourceType: 'seed_bank',
species: 'Blockchain primordialis',
quantity: 1,
quantityUnit: 'seeds',
generation: 0
};
return {
index: 0,
timestamp: new Date().toISOString(),
transportEvent: genesisEvent,
previousHash: '0',
hash: this.calculateHash(0, new Date().toISOString(), genesisEvent, '0', 0),
nonce: 0,
cumulativeCarbonKg: 0,
cumulativeFoodMiles: 0,
chainLength: 1
};
}
private calculateHash(
index: number,
timestamp: string,
event: TransportEvent,
previousHash: string,
nonce: number
): string {
const data = `${index}${timestamp}${JSON.stringify(event)}${previousHash}${nonce}`;
return crypto.createHash('sha256').update(data).digest('hex');
}
private mineBlock(block: TransportBlock): void {
const target = '0'.repeat(this.difficulty);
while (block.hash.substring(0, this.difficulty) !== target) {
block.nonce++;
block.hash = this.calculateHash(
block.index,
block.timestamp,
block.transportEvent,
block.previousHash,
block.nonce
);
}
}
getLatestBlock(): TransportBlock {
return this.chain[this.chain.length - 1];
}
/**
* Record a new transport event
*/
recordEvent(event: TransportEvent): TransportBlock {
const latestBlock = this.getLatestBlock();
// Calculate carbon footprint if not provided
if (!event.carbonFootprintKg) {
event.carbonFootprintKg = this.calculateCarbon(
event.transportMethod,
event.distanceKm,
this.estimateWeight(event)
);
}
const newBlock: TransportBlock = {
index: this.chain.length,
timestamp: new Date().toISOString(),
transportEvent: event,
previousHash: latestBlock.hash,
hash: '',
nonce: 0,
cumulativeCarbonKg: latestBlock.cumulativeCarbonKg + event.carbonFootprintKg,
cumulativeFoodMiles: latestBlock.cumulativeFoodMiles + event.distanceKm,
chainLength: this.chain.length + 1
};
newBlock.hash = this.calculateHash(
newBlock.index,
newBlock.timestamp,
event,
newBlock.previousHash,
newBlock.nonce
);
this.mineBlock(newBlock);
this.chain.push(newBlock);
this.indexEvent(event, newBlock);
return newBlock;
}
private indexEvent(event: TransportEvent, block: TransportBlock): void {
// Index by event ID
const eventBlocks = this.eventIndex.get(event.id) || [];
eventBlocks.push(block);
this.eventIndex.set(event.id, eventBlocks);
// Index by plant IDs
const plantIds = this.extractPlantIds(event);
for (const plantId of plantIds) {
const events = this.plantEvents.get(plantId) || [];
events.push(event);
this.plantEvents.set(plantId, events);
}
// Index by batch IDs
const batchIds = this.extractBatchIds(event);
for (const batchId of batchIds) {
const events = this.batchEvents.get(batchId) || [];
events.push(event);
this.batchEvents.set(batchId, events);
}
}
private extractPlantIds(event: TransportEvent): string[] {
switch (event.eventType) {
case 'planting':
return (event as PlantingEvent).plantIds;
case 'growing_transport':
return (event as GrowingTransportEvent).plantIds;
case 'harvest':
return (event as HarvestEvent).plantIds;
case 'seed_saving':
return (event as SeedSavingEvent).parentPlantIds;
default:
return [];
}
}
private extractBatchIds(event: TransportEvent): string[] {
const batchIds: string[] = [];
switch (event.eventType) {
case 'seed_acquisition':
batchIds.push((event as SeedAcquisitionEvent).seedBatchId);
break;
case 'planting':
batchIds.push((event as PlantingEvent).seedBatchId);
break;
case 'harvest':
batchIds.push((event as HarvestEvent).harvestBatchId);
if ((event as HarvestEvent).seedBatchIdCreated) {
batchIds.push((event as HarvestEvent).seedBatchIdCreated!);
}
break;
case 'processing':
batchIds.push(...(event as ProcessingEvent).harvestBatchIds);
batchIds.push((event as ProcessingEvent).processingBatchId);
break;
case 'distribution':
batchIds.push(...(event as DistributionEvent).batchIds);
break;
case 'consumer_delivery':
batchIds.push(...(event as ConsumerDeliveryEvent).batchIds);
break;
case 'seed_saving':
batchIds.push((event as SeedSavingEvent).newSeedBatchId);
break;
case 'seed_sharing':
batchIds.push((event as SeedSharingEvent).seedBatchId);
break;
}
return batchIds;
}
private estimateWeight(event: TransportEvent): number {
// Estimate weight in kg based on event type
switch (event.eventType) {
case 'seed_acquisition':
case 'seed_saving':
case 'seed_sharing':
return 0.1; // Seeds are light
case 'planting':
return 0.5;
case 'growing_transport':
return 2;
case 'harvest':
return (event as HarvestEvent).netWeight || 5;
case 'processing':
return (event as ProcessingEvent).outputWeight || 5;
case 'distribution':
case 'consumer_delivery':
return 5;
default:
return 1;
}
}
/**
* Calculate carbon footprint
*/
calculateCarbon(method: TransportMethod, distanceKm: number, weightKg: number): number {
const factor = CARBON_FACTORS[method] || 0.1;
return factor * distanceKm * weightKg;
}
/**
* Calculate distance between two locations using Haversine formula
*/
static calculateDistance(from: TransportLocation, to: TransportLocation): number {
const R = 6371; // Earth's radius in km
const dLat = TransportChain.toRadians(to.latitude - from.latitude);
const dLon = TransportChain.toRadians(to.longitude - from.longitude);
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(TransportChain.toRadians(from.latitude)) *
Math.cos(TransportChain.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;
}
private static toRadians(degrees: number): number {
return degrees * (Math.PI / 180);
}
/**
* Get complete journey for a plant
*/
getPlantJourney(plantId: string): PlantJourney | null {
const events = this.plantEvents.get(plantId);
if (!events || events.length === 0) return null;
// Sort events by timestamp
const sortedEvents = [...events].sort(
(a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
);
const lastEvent = sortedEvents[sortedEvents.length - 1];
// Calculate metrics
let totalFoodMiles = 0;
let totalCarbonKg = 0;
let daysInTransit = 0;
for (const event of sortedEvents) {
totalFoodMiles += event.distanceKm;
totalCarbonKg += event.carbonFootprintKg;
daysInTransit += event.durationMinutes / (60 * 24);
}
// Find seed batch origin
const seedAcquisition = sortedEvents.find(e => e.eventType === 'seed_acquisition') as SeedAcquisitionEvent | undefined;
const planting = sortedEvents.find(e => e.eventType === 'planting') as PlantingEvent | undefined;
// Calculate growing days
const plantingDate = planting ? new Date(planting.timestamp) : null;
const lastDate = new Date(lastEvent.timestamp);
const daysGrowing = plantingDate
? Math.floor((lastDate.getTime() - plantingDate.getTime()) / (1000 * 60 * 60 * 24))
: 0;
// Determine current stage
let currentStage: PlantJourney['currentStage'] = 'seed';
if (sortedEvents.some(e => e.eventType === 'seed_saving')) {
currentStage = 'seed_saving';
} else if (sortedEvents.some(e => e.eventType === 'harvest')) {
currentStage = 'post_harvest';
} else if (sortedEvents.some(e => e.eventType === 'growing_transport')) {
const lastGrowing = sortedEvents.filter(e => e.eventType === 'growing_transport').pop() as GrowingTransportEvent;
currentStage = lastGrowing.plantStage;
} else if (sortedEvents.some(e => e.eventType === 'planting')) {
currentStage = 'seedling';
}
return {
plantId,
seedBatchOrigin: seedAcquisition?.seedBatchId || planting?.seedBatchId || 'unknown',
currentCustodian: lastEvent.receiverId,
currentLocation: lastEvent.toLocation,
currentStage,
events: sortedEvents,
totalFoodMiles,
totalCarbonKg,
daysInTransit: Math.round(daysInTransit),
daysGrowing,
generation: seedAcquisition?.generation || 0,
ancestorPlantIds: seedAcquisition?.parentPlantIds || [],
descendantSeedBatches: sortedEvents
.filter(e => e.eventType === 'seed_saving')
.map(e => (e as SeedSavingEvent).newSeedBatchId)
};
}
/**
* Get environmental impact summary for a user
*/
getEnvironmentalImpact(userId: string): EnvironmentalImpact {
const userEvents = this.chain
.filter(block =>
block.transportEvent.senderId === userId ||
block.transportEvent.receiverId === userId
)
.map(block => block.transportEvent);
let totalCarbonKg = 0;
let totalFoodMiles = 0;
let totalWeight = 0;
const breakdownByMethod: EnvironmentalImpact['breakdownByMethod'] = {} as any;
const breakdownByEventType: EnvironmentalImpact['breakdownByEventType'] = {} as any;
for (const event of userEvents) {
totalCarbonKg += event.carbonFootprintKg;
totalFoodMiles += event.distanceKm;
totalWeight += this.estimateWeight(event);
// Method breakdown
if (!breakdownByMethod[event.transportMethod]) {
breakdownByMethod[event.transportMethod] = { distance: 0, carbon: 0 };
}
breakdownByMethod[event.transportMethod].distance += event.distanceKm;
breakdownByMethod[event.transportMethod].carbon += event.carbonFootprintKg;
// Event type breakdown
if (!breakdownByEventType[event.eventType]) {
breakdownByEventType[event.eventType] = { count: 0, carbon: 0 };
}
breakdownByEventType[event.eventType].count++;
breakdownByEventType[event.eventType].carbon += event.carbonFootprintKg;
}
// Conventional comparison (assume 1500 miles avg, 2.5 kg CO2/lb)
const conventionalMiles = totalWeight * 1500;
const conventionalCarbon = totalWeight * 2.5;
return {
totalCarbonKg,
totalFoodMiles,
carbonPerKgProduce: totalWeight > 0 ? totalCarbonKg / totalWeight : 0,
milesPerKgProduce: totalWeight > 0 ? totalFoodMiles / totalWeight : 0,
breakdownByMethod,
breakdownByEventType,
comparisonToConventional: {
carbonSaved: Math.max(0, conventionalCarbon - totalCarbonKg),
milesSaved: Math.max(0, conventionalMiles - totalFoodMiles),
percentageReduction: conventionalCarbon > 0
? Math.round((1 - totalCarbonKg / conventionalCarbon) * 100)
: 0
}
};
}
/**
* Generate QR code data for a plant or batch
*/
generateQRData(plantId?: string, batchId?: string): TransportQRData {
const events = plantId
? this.plantEvents.get(plantId)
: batchId
? this.batchEvents.get(batchId)
: [];
const lastEvent = events && events.length > 0
? events[events.length - 1]
: null;
const lineageHash = crypto.createHash('sha256')
.update(JSON.stringify(events))
.digest('hex')
.substring(0, 16);
return {
plantId,
batchId,
blockchainAddress: this.getLatestBlock().hash.substring(0, 42),
quickLookupUrl: `https://localgreenchain.org/track/${plantId || batchId}`,
lineageHash,
currentCustodian: lastEvent?.receiverId || 'unknown',
lastEventType: lastEvent?.eventType || 'seed_acquisition',
lastEventTimestamp: lastEvent?.timestamp || new Date().toISOString(),
verificationCode: crypto.randomBytes(4).toString('hex').toUpperCase()
};
}
/**
* Verify chain integrity
*/
isChainValid(): boolean {
for (let i = 1; i < this.chain.length; i++) {
const currentBlock = this.chain[i];
const previousBlock = this.chain[i - 1];
// Verify hash
const expectedHash = this.calculateHash(
currentBlock.index,
currentBlock.timestamp,
currentBlock.transportEvent,
currentBlock.previousHash,
currentBlock.nonce
);
if (currentBlock.hash !== expectedHash) {
return false;
}
// Verify chain link
if (currentBlock.previousHash !== previousBlock.hash) {
return false;
}
}
return true;
}
/**
* Export to JSON
*/
toJSON(): object {
return {
difficulty: this.difficulty,
chain: this.chain
};
}
/**
* Import from JSON
*/
static fromJSON(data: any): TransportChain {
const chain = new TransportChain(data.difficulty);
chain.chain = data.chain;
// Rebuild indexes
for (const block of chain.chain) {
chain.indexEvent(block.transportEvent, block);
}
return chain;
}
}
// Singleton instance
let transportChainInstance: TransportChain | null = null;
export function getTransportChain(): TransportChain {
if (!transportChainInstance) {
transportChainInstance = new TransportChain();
}
return transportChainInstance;
}
export function setTransportChain(chain: TransportChain): void {
transportChainInstance = chain;
}

431
lib/transport/types.ts Normal file
View file

@ -0,0 +1,431 @@
/**
* Transport Tracking Types for LocalGreenChain
* Seed-to-Seed transport event tracking with blockchain integration
*/
// Transport Location with full traceability
export interface TransportLocation {
latitude: number;
longitude: number;
address?: string;
city?: string;
region?: string;
country?: string;
postalCode?: string;
locationType: 'farm' | 'greenhouse' | 'vertical_farm' | 'warehouse' | 'hub' | 'market' | 'consumer' | 'seed_bank' | 'other';
facilityId?: string;
facilityName?: string;
}
// Transport method definitions
export type TransportMethod =
| 'walking'
| 'bicycle'
| 'electric_vehicle'
| 'hybrid_vehicle'
| 'gasoline_vehicle'
| 'diesel_truck'
| 'electric_truck'
| 'refrigerated_truck'
| 'rail'
| 'ship'
| 'air'
| 'drone'
| 'local_delivery'
| 'customer_pickup';
// Carbon emission factors (kg CO2 per km per kg of cargo)
export const CARBON_FACTORS: Record<TransportMethod, number> = {
walking: 0,
bicycle: 0,
electric_vehicle: 0.02,
hybrid_vehicle: 0.08,
gasoline_vehicle: 0.12,
diesel_truck: 0.15,
electric_truck: 0.03,
refrigerated_truck: 0.25,
rail: 0.01,
ship: 0.008,
air: 0.5,
drone: 0.01,
local_delivery: 0.05,
customer_pickup: 0.1
};
// Plant lifecycle stages
export type PlantStage =
| 'seed'
| 'germinating'
| 'seedling'
| 'vegetative'
| 'flowering'
| 'fruiting'
| 'mature'
| 'harvesting'
| 'post_harvest'
| 'seed_saving';
// Environmental conditions during transport
export interface TransportEnvironment {
temperatureMin: number;
temperatureMax: number;
temperatureAvg: number;
humidityMin: number;
humidityMax: number;
humidityAvg: number;
lightExposure: 'dark' | 'low' | 'ambient' | 'bright';
shockEvents?: number;
handlingNotes?: string;
}
// Temperature requirements for produce
export interface TemperatureRange {
min: number;
max: number;
optimal: number;
unit: 'celsius' | 'fahrenheit';
}
// Seed storage conditions
export interface SeedStorageConditions {
temperature: number;
humidity: number;
lightExposure: 'dark' | 'minimal';
containerType: 'envelope' | 'jar' | 'vacuum_sealed' | 'moisture_proof' | 'seed_vault';
desiccant: boolean;
estimatedViability: number; // years
}
// Base transport event
export interface BaseTransportEvent {
id: string;
timestamp: string;
eventType: TransportEventType;
// Locations
fromLocation: TransportLocation;
toLocation: TransportLocation;
// Distance and duration
distanceKm: number;
durationMinutes: number;
// Environmental impact
transportMethod: TransportMethod;
carbonFootprintKg: number;
// Verification
senderId: string;
receiverId: string;
senderSignature?: string;
receiverSignature?: string;
verifierSignature?: string;
// Status
status: 'pending' | 'in_transit' | 'delivered' | 'verified' | 'disputed';
// Metadata
notes?: string;
photos?: string[];
documents?: string[];
}
export type TransportEventType =
| 'seed_acquisition'
| 'planting'
| 'growing_transport'
| 'harvest'
| 'processing'
| 'distribution'
| 'consumer_delivery'
| 'seed_saving'
| 'seed_sharing';
// Seed Acquisition Transport Event
export interface SeedAcquisitionEvent extends BaseTransportEvent {
eventType: 'seed_acquisition';
seedBatchId: string;
sourceType: 'seed_bank' | 'previous_harvest' | 'trade' | 'purchase' | 'wild_collected' | 'gift';
// Seed details
species: string;
variety?: string;
quantity: number;
quantityUnit: 'seeds' | 'grams' | 'ounces' | 'packets';
// Lineage
geneticLineageId?: string;
parentPlantIds?: string[];
generation: number;
// Quality
germinationRate?: number;
purityPercentage?: number;
harvestDate?: string;
expirationDate?: string;
// Certifications
certifications?: ('organic' | 'non_gmo' | 'heirloom' | 'certified_seed' | 'biodynamic')[];
certificationDocuments?: string[];
}
// Planting Event
export interface PlantingEvent extends BaseTransportEvent {
eventType: 'planting';
seedBatchId: string;
plantIds: string[]; // New plant IDs created
plantingMethod: 'direct_sow' | 'transplant' | 'indoor_start' | 'hydroponic' | 'aeroponic';
sowingDepth?: number; // mm
spacing?: number; // cm
quantityPlanted: number;
growingEnvironment: 'outdoor' | 'greenhouse' | 'indoor' | 'vertical_farm';
expectedHarvestDate?: string;
}
// Growing Transport Event (transplanting, moving plants)
export interface GrowingTransportEvent extends BaseTransportEvent {
eventType: 'growing_transport';
plantIds: string[];
reason: 'transplant' | 'relocation' | 'hardening_off' | 'seasonal_move' | 'upgrade_facility';
plantStage: PlantStage;
handlingMethod: 'bare_root' | 'potted' | 'root_ball' | 'hydroponic_transfer' | 'aeroponic_transfer';
rootDisturbance: 'none' | 'minimal' | 'moderate' | 'significant';
acclimatizationRequired: boolean;
acclimatizationDays?: number;
}
// Harvest Event
export interface HarvestEvent extends BaseTransportEvent {
eventType: 'harvest';
plantIds: string[];
harvestBatchId: string;
harvestType: 'full' | 'partial' | 'continuous' | 'selective';
produceType: string; // e.g., "tomatoes", "lettuce heads", "basil leaves"
// Quantities
grossWeight: number;
netWeight: number;
weightUnit: 'kg' | 'lbs' | 'oz' | 'grams';
itemCount?: number;
// Quality
qualityGrade?: 'A' | 'B' | 'C' | 'processing';
qualityNotes?: string;
// Storage
packagingType: string;
temperatureRequired: TemperatureRange;
shelfLifeHours: number;
// For seed saving
seedsSaved: boolean;
seedBatchIdCreated?: string;
}
// Processing Event
export interface ProcessingEvent extends BaseTransportEvent {
eventType: 'processing';
harvestBatchIds: string[];
processingBatchId: string;
processingType: 'washing' | 'cutting' | 'packaging' | 'freezing' | 'drying' | 'canning' | 'juicing' | 'fermenting';
inputWeight: number;
outputWeight: number;
weightUnit: 'kg' | 'lbs';
wasteWeight: number;
wasteDisposal: 'compost' | 'animal_feed' | 'biogas' | 'landfill';
outputProducts: ProcessedProduct[];
}
export interface ProcessedProduct {
productId: string;
productType: string;
quantity: number;
unit: string;
shelfLifeDays: number;
storageRequirements: string;
}
// Distribution Event
export interface DistributionEvent extends BaseTransportEvent {
eventType: 'distribution';
batchIds: string[]; // harvest or processing batch IDs
destinationType: 'consumer' | 'market' | 'restaurant' | 'processor' | 'distributor' | 'vertical_farm';
// Order info
orderId?: string;
customerType: 'individual' | 'business' | 'cooperative' | 'institution';
// Delivery
deliveryWindow: {
start: string;
end: string;
};
actualDeliveryTime?: string;
deliveryAttempts: number;
// Chain of custody
handoffVerified: boolean;
recipientName?: string;
recipientSignature?: string;
}
// Consumer Delivery Event
export interface ConsumerDeliveryEvent extends BaseTransportEvent {
eventType: 'consumer_delivery';
orderId: string;
batchIds: string[];
deliveryMethod: 'home_delivery' | 'pickup_point' | 'farmers_market' | 'csa_distribution' | 'restaurant_delivery';
// Final mile details
finalMileMethod: TransportMethod;
packagingReturned: boolean;
feedbackReceived: boolean;
feedbackRating?: number;
feedbackNotes?: string;
}
// Seed Saving Event
export interface SeedSavingEvent extends BaseTransportEvent {
eventType: 'seed_saving';
parentPlantIds: string[];
newSeedBatchId: string;
// Seed collection
collectionMethod: 'dry_seed' | 'wet_seed' | 'fermentation' | 'threshing';
seedCount?: number;
seedWeight?: number;
seedWeightUnit?: 'grams' | 'ounces';
// Quality testing
viabilityTestDate?: string;
germinationRate?: number;
purityTest?: boolean;
purityPercentage?: number;
// Storage
storageConditions: SeedStorageConditions;
storageLocationId: string;
// Lineage
newGenerationNumber: number;
geneticNotes?: string;
// Sharing plans
availableForSharing: boolean;
sharingTerms?: 'free' | 'trade' | 'sale' | 'restricted';
}
// Seed Sharing Event
export interface SeedSharingEvent extends BaseTransportEvent {
eventType: 'seed_sharing';
seedBatchId: string;
quantityShared: number;
quantityUnit: 'seeds' | 'grams' | 'packets';
sharingType: 'gift' | 'trade' | 'sale' | 'seed_library';
tradeDetails?: string;
saleAmount?: number;
saleCurrency?: string;
recipientAgreement: boolean;
growingCommitment?: string;
reportBackRequired: boolean;
}
// Union type for all transport events
export type TransportEvent =
| SeedAcquisitionEvent
| PlantingEvent
| GrowingTransportEvent
| HarvestEvent
| ProcessingEvent
| DistributionEvent
| ConsumerDeliveryEvent
| SeedSavingEvent
| SeedSharingEvent;
// Transport Block for blockchain
export interface TransportBlock {
index: number;
timestamp: string;
transportEvent: TransportEvent;
previousHash: string;
hash: string;
nonce: number;
// Cumulative metrics
cumulativeCarbonKg: number;
cumulativeFoodMiles: number;
chainLength: number;
}
// Plant Journey - complete transport history
export interface PlantJourney {
plantId: string;
seedBatchOrigin: string;
currentCustodian: string;
currentLocation: TransportLocation;
currentStage: PlantStage;
events: TransportEvent[];
// Metrics
totalFoodMiles: number;
totalCarbonKg: number;
daysInTransit: number;
daysGrowing: number;
// Lineage
generation: number;
ancestorPlantIds: string[];
descendantSeedBatches: string[];
}
// Environmental Impact Summary
export interface EnvironmentalImpact {
totalCarbonKg: number;
totalFoodMiles: number;
carbonPerKgProduce: number;
milesPerKgProduce: number;
breakdownByMethod: Record<TransportMethod, { distance: number; carbon: number }>;
breakdownByEventType: Record<TransportEventType, { count: number; carbon: number }>;
comparisonToConventional: {
carbonSaved: number;
milesSaved: number;
percentageReduction: number;
};
}
// QR Code Data
export interface TransportQRData {
plantId?: string;
batchId?: string;
blockchainAddress: string;
quickLookupUrl: string;
lineageHash: string;
currentCustodian: string;
lastEventType: TransportEventType;
lastEventTimestamp: string;
verificationCode: string;
}

View file

@ -0,0 +1,759 @@
/**
* Vertical Farm Controller for LocalGreenChain
* Manages vertical farm operations, automation, and optimization
*/
import {
VerticalFarm,
GrowingZone,
ZoneEnvironmentTargets,
ZoneEnvironmentReadings,
EnvironmentAlert,
GrowingRecipe,
CropBatch,
ResourceUsage,
FarmAnalytics,
LightSchedule,
NutrientRecipe,
BatchIssue
} from './types';
/**
* Vertical Farm Controller
*/
export class VerticalFarmController {
private farms: Map<string, VerticalFarm> = new Map();
private recipes: Map<string, GrowingRecipe> = new Map();
private batches: Map<string, CropBatch> = new Map();
private resourceLogs: Map<string, ResourceUsage[]> = new Map();
constructor() {
this.initializeDefaultRecipes();
}
/**
* Initialize default growing recipes
*/
private initializeDefaultRecipes(): void {
const defaultRecipes: GrowingRecipe[] = [
{
id: 'recipe-lettuce-butterhead',
name: 'Butterhead Lettuce - Fast Cycle',
cropType: 'lettuce',
variety: 'butterhead',
version: '1.0',
stages: [
{
name: 'Germination',
daysStart: 0,
daysEnd: 3,
temperature: { day: 20, night: 18 },
humidity: { day: 80, night: 85 },
co2Ppm: 800,
lightHours: 18,
lightPpfd: 150,
nutrientRecipeId: 'nutrient-seedling',
targetEc: 0.8,
targetPh: 6.0,
actions: []
},
{
name: 'Seedling',
daysStart: 4,
daysEnd: 10,
temperature: { day: 21, night: 18 },
humidity: { day: 70, night: 75 },
co2Ppm: 1000,
lightHours: 18,
lightPpfd: 200,
nutrientRecipeId: 'nutrient-vegetative',
targetEc: 1.2,
targetPh: 6.0,
actions: [
{ day: 10, action: 'transplant', description: 'Transplant to final position', automated: true }
]
},
{
name: 'Vegetative Growth',
daysStart: 11,
daysEnd: 28,
temperature: { day: 22, night: 18 },
humidity: { day: 65, night: 70 },
co2Ppm: 1200,
lightHours: 16,
lightPpfd: 300,
nutrientRecipeId: 'nutrient-vegetative',
targetEc: 1.6,
targetPh: 6.0,
actions: []
},
{
name: 'Finishing',
daysStart: 29,
daysEnd: 35,
temperature: { day: 20, night: 16 },
humidity: { day: 60, night: 65 },
co2Ppm: 800,
lightHours: 14,
lightPpfd: 250,
nutrientRecipeId: 'nutrient-finishing',
targetEc: 1.2,
targetPh: 6.0,
actions: []
}
],
expectedDays: 35,
expectedYieldGrams: 180,
expectedYieldPerSqm: 4000,
requirements: {
positions: 1,
zoneType: 'NFT',
minimumPpfd: 200,
idealTemperatureC: 21
},
source: 'internal',
rating: 4.5,
timesUsed: 0
},
{
id: 'recipe-basil-genovese',
name: 'Genovese Basil - Aromatic',
cropType: 'basil',
variety: 'genovese',
version: '1.0',
stages: [
{
name: 'Germination',
daysStart: 0,
daysEnd: 5,
temperature: { day: 24, night: 22 },
humidity: { day: 80, night: 85 },
co2Ppm: 800,
lightHours: 16,
lightPpfd: 100,
nutrientRecipeId: 'nutrient-seedling',
targetEc: 0.6,
targetPh: 6.2,
actions: []
},
{
name: 'Seedling',
daysStart: 6,
daysEnd: 14,
temperature: { day: 25, night: 22 },
humidity: { day: 70, night: 75 },
co2Ppm: 1000,
lightHours: 18,
lightPpfd: 200,
nutrientRecipeId: 'nutrient-vegetative',
targetEc: 1.0,
targetPh: 6.2,
actions: [
{ day: 14, action: 'transplant', description: 'Transplant to growing system', automated: true }
]
},
{
name: 'Vegetative',
daysStart: 15,
daysEnd: 35,
temperature: { day: 26, night: 22 },
humidity: { day: 65, night: 70 },
co2Ppm: 1200,
lightHours: 18,
lightPpfd: 400,
nutrientRecipeId: 'nutrient-herbs',
targetEc: 1.4,
targetPh: 6.0,
actions: [
{ day: 25, action: 'top', description: 'Top plants to encourage bushiness', automated: false }
]
},
{
name: 'Harvest Ready',
daysStart: 36,
daysEnd: 42,
temperature: { day: 24, night: 20 },
humidity: { day: 60, night: 65 },
co2Ppm: 800,
lightHours: 16,
lightPpfd: 350,
nutrientRecipeId: 'nutrient-finishing',
targetEc: 1.0,
targetPh: 6.0,
actions: []
}
],
expectedDays: 42,
expectedYieldGrams: 120,
expectedYieldPerSqm: 2400,
requirements: {
positions: 1,
zoneType: 'NFT',
minimumPpfd: 300,
idealTemperatureC: 25
},
source: 'internal',
rating: 4.3,
timesUsed: 0
},
{
id: 'recipe-microgreens-mix',
name: 'Microgreens Mix - Quick Turn',
cropType: 'microgreens',
variety: 'mixed',
version: '1.0',
stages: [
{
name: 'Sowing',
daysStart: 0,
daysEnd: 2,
temperature: { day: 22, night: 20 },
humidity: { day: 90, night: 90 },
co2Ppm: 600,
lightHours: 0,
lightPpfd: 0,
nutrientRecipeId: 'nutrient-none',
targetEc: 0,
targetPh: 6.0,
actions: []
},
{
name: 'Germination',
daysStart: 3,
daysEnd: 5,
temperature: { day: 22, night: 20 },
humidity: { day: 80, night: 85 },
co2Ppm: 800,
lightHours: 12,
lightPpfd: 100,
nutrientRecipeId: 'nutrient-none',
targetEc: 0,
targetPh: 6.0,
actions: []
},
{
name: 'Growth',
daysStart: 6,
daysEnd: 12,
temperature: { day: 21, night: 19 },
humidity: { day: 65, night: 70 },
co2Ppm: 1000,
lightHours: 16,
lightPpfd: 250,
nutrientRecipeId: 'nutrient-microgreens',
targetEc: 0.8,
targetPh: 6.0,
actions: []
},
{
name: 'Harvest',
daysStart: 13,
daysEnd: 14,
temperature: { day: 20, night: 18 },
humidity: { day: 60, night: 65 },
co2Ppm: 600,
lightHours: 14,
lightPpfd: 200,
nutrientRecipeId: 'nutrient-none',
targetEc: 0,
targetPh: 6.0,
actions: []
}
],
expectedDays: 14,
expectedYieldGrams: 200,
expectedYieldPerSqm: 2000,
requirements: {
positions: 1,
zoneType: 'rack_system',
minimumPpfd: 150,
idealTemperatureC: 21
},
source: 'internal',
rating: 4.7,
timesUsed: 0
}
];
for (const recipe of defaultRecipes) {
this.recipes.set(recipe.id, recipe);
}
}
/**
* Register a new vertical farm
*/
registerFarm(farm: VerticalFarm): void {
this.farms.set(farm.id, farm);
this.resourceLogs.set(farm.id, []);
}
/**
* Get farm by ID
*/
getFarm(farmId: string): VerticalFarm | undefined {
return this.farms.get(farmId);
}
/**
* Start a new crop batch
*/
startCropBatch(
farmId: string,
zoneId: string,
recipeId: string,
seedBatchId: string,
plantCount: number
): CropBatch {
const farm = this.farms.get(farmId);
if (!farm) throw new Error(`Farm ${farmId} not found`);
const zone = farm.zones.find(z => z.id === zoneId);
if (!zone) throw new Error(`Zone ${zoneId} not found in farm ${farmId}`);
const recipe = this.recipes.get(recipeId);
if (!recipe) throw new Error(`Recipe ${recipeId} not found`);
const now = new Date();
const expectedHarvest = new Date(now.getTime() + recipe.expectedDays * 24 * 60 * 60 * 1000);
const batch: CropBatch = {
id: `batch-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
farmId,
zoneId,
cropType: recipe.cropType,
variety: recipe.variety,
recipeId,
seedBatchId,
plantIds: [],
plantCount,
plantingDate: now.toISOString(),
currentStage: recipe.stages[0].name,
currentDay: 0,
healthScore: 100,
expectedHarvestDate: expectedHarvest.toISOString(),
expectedYieldKg: (recipe.expectedYieldGrams * plantCount) / 1000,
status: 'germinating',
issues: [],
environmentLog: []
};
// Generate plant IDs
for (let i = 0; i < plantCount; i++) {
batch.plantIds.push(`${batch.id}-plant-${i}`);
}
// Update zone
zone.currentCrop = recipe.cropType;
zone.plantIds = batch.plantIds;
zone.plantingDate = batch.plantingDate;
zone.expectedHarvestDate = batch.expectedHarvestDate;
zone.status = 'planted';
// Set environment targets from recipe
const firstStage = recipe.stages[0];
zone.environmentTargets = {
temperatureC: { min: firstStage.temperature.night - 2, max: firstStage.temperature.day + 2, target: firstStage.temperature.day },
humidityPercent: { min: firstStage.humidity.day - 10, max: firstStage.humidity.night + 5, target: firstStage.humidity.day },
co2Ppm: { min: firstStage.co2Ppm - 200, max: firstStage.co2Ppm + 200, target: firstStage.co2Ppm },
lightPpfd: { min: firstStage.lightPpfd * 0.8, max: firstStage.lightPpfd * 1.2, target: firstStage.lightPpfd },
lightHours: firstStage.lightHours,
nutrientEc: { min: firstStage.targetEc - 0.2, max: firstStage.targetEc + 0.2, target: firstStage.targetEc },
nutrientPh: { min: firstStage.targetPh - 0.3, max: firstStage.targetPh + 0.3, target: firstStage.targetPh },
waterTempC: { min: 18, max: 24, target: 20 }
};
this.batches.set(batch.id, batch);
recipe.timesUsed++;
return batch;
}
/**
* Update batch progress
*/
updateBatchProgress(batchId: string): CropBatch {
const batch = this.batches.get(batchId);
if (!batch) throw new Error(`Batch ${batchId} not found`);
const recipe = this.recipes.get(batch.recipeId);
if (!recipe) throw new Error(`Recipe ${batch.recipeId} not found`);
const plantingDate = new Date(batch.plantingDate);
const now = new Date();
batch.currentDay = Math.floor((now.getTime() - plantingDate.getTime()) / (24 * 60 * 60 * 1000));
// Determine current stage
for (const stage of recipe.stages) {
if (batch.currentDay >= stage.daysStart && batch.currentDay <= stage.daysEnd) {
batch.currentStage = stage.name;
// Update zone targets
const farm = this.farms.get(batch.farmId);
const zone = farm?.zones.find(z => z.id === batch.zoneId);
if (zone) {
zone.environmentTargets = {
temperatureC: { min: stage.temperature.night - 2, max: stage.temperature.day + 2, target: stage.temperature.day },
humidityPercent: { min: stage.humidity.day - 10, max: stage.humidity.night + 5, target: stage.humidity.day },
co2Ppm: { min: stage.co2Ppm - 200, max: stage.co2Ppm + 200, target: stage.co2Ppm },
lightPpfd: { min: stage.lightPpfd * 0.8, max: stage.lightPpfd * 1.2, target: stage.lightPpfd },
lightHours: stage.lightHours,
nutrientEc: { min: stage.targetEc - 0.2, max: stage.targetEc + 0.2, target: stage.targetEc },
nutrientPh: { min: stage.targetPh - 0.3, max: stage.targetPh + 0.3, target: stage.targetPh },
waterTempC: { min: 18, max: 24, target: 20 }
};
}
break;
}
}
// Update status
if (batch.currentDay >= recipe.expectedDays) {
batch.status = 'ready';
} else if (batch.currentDay > 3) {
batch.status = 'growing';
}
return batch;
}
/**
* Record environment reading
*/
recordEnvironment(zoneId: string, readings: ZoneEnvironmentReadings): EnvironmentAlert[] {
const alerts: EnvironmentAlert[] = [];
// Find the zone
let targetZone: GrowingZone | undefined;
let farm: VerticalFarm | undefined;
for (const f of this.farms.values()) {
const zone = f.zones.find(z => z.id === zoneId);
if (zone) {
targetZone = zone;
farm = f;
break;
}
}
if (!targetZone || !farm) return alerts;
const targets = targetZone.environmentTargets;
// Check temperature
if (readings.temperatureC < targets.temperatureC.min) {
alerts.push({
parameter: 'temperature',
type: readings.temperatureC < targets.temperatureC.min - 5 ? 'critical_low' : 'low',
value: readings.temperatureC,
threshold: targets.temperatureC.min,
timestamp: readings.timestamp,
acknowledged: false
});
} else if (readings.temperatureC > targets.temperatureC.max) {
alerts.push({
parameter: 'temperature',
type: readings.temperatureC > targets.temperatureC.max + 5 ? 'critical_high' : 'high',
value: readings.temperatureC,
threshold: targets.temperatureC.max,
timestamp: readings.timestamp,
acknowledged: false
});
}
// Check humidity
if (readings.humidityPercent < targets.humidityPercent.min) {
alerts.push({
parameter: 'humidity',
type: 'low',
value: readings.humidityPercent,
threshold: targets.humidityPercent.min,
timestamp: readings.timestamp,
acknowledged: false
});
} else if (readings.humidityPercent > targets.humidityPercent.max) {
alerts.push({
parameter: 'humidity',
type: 'high',
value: readings.humidityPercent,
threshold: targets.humidityPercent.max,
timestamp: readings.timestamp,
acknowledged: false
});
}
// Check EC
if (readings.ec < targets.nutrientEc.min) {
alerts.push({
parameter: 'ec',
type: 'low',
value: readings.ec,
threshold: targets.nutrientEc.min,
timestamp: readings.timestamp,
acknowledged: false
});
} else if (readings.ec > targets.nutrientEc.max) {
alerts.push({
parameter: 'ec',
type: 'high',
value: readings.ec,
threshold: targets.nutrientEc.max,
timestamp: readings.timestamp,
acknowledged: false
});
}
// Check pH
if (readings.ph < targets.nutrientPh.min) {
alerts.push({
parameter: 'ph',
type: 'low',
value: readings.ph,
threshold: targets.nutrientPh.min,
timestamp: readings.timestamp,
acknowledged: false
});
} else if (readings.ph > targets.nutrientPh.max) {
alerts.push({
parameter: 'ph',
type: 'high',
value: readings.ph,
threshold: targets.nutrientPh.max,
timestamp: readings.timestamp,
acknowledged: false
});
}
// Update zone readings
readings.alerts = alerts;
targetZone.currentEnvironment = readings;
// Log to batch if exists
const batch = Array.from(this.batches.values()).find(b =>
b.zoneId === zoneId && b.status !== 'completed' && b.status !== 'failed'
);
if (batch) {
batch.environmentLog.push({
timestamp: readings.timestamp,
readings
});
// Adjust health score based on alerts
if (alerts.some(a => a.type.includes('critical'))) {
batch.healthScore = Math.max(0, batch.healthScore - 5);
} else if (alerts.length > 0) {
batch.healthScore = Math.max(0, batch.healthScore - 1);
}
}
return alerts;
}
/**
* Complete harvest
*/
completeHarvest(batchId: string, actualYieldKg: number, qualityGrade: string): CropBatch {
const batch = this.batches.get(batchId);
if (!batch) throw new Error(`Batch ${batchId} not found`);
batch.actualHarvestDate = new Date().toISOString();
batch.actualYieldKg = actualYieldKg;
batch.qualityGrade = qualityGrade;
batch.status = 'completed';
// Update zone
const farm = this.farms.get(batch.farmId);
const zone = farm?.zones.find(z => z.id === batch.zoneId);
if (zone) {
zone.status = 'cleaning';
zone.currentCrop = '';
zone.plantIds = [];
}
return batch;
}
/**
* Generate farm analytics
*/
generateAnalytics(farmId: string, periodDays: number = 30): FarmAnalytics {
const farm = this.farms.get(farmId);
if (!farm) throw new Error(`Farm ${farmId} not found`);
const now = new Date();
const periodStart = new Date(now.getTime() - periodDays * 24 * 60 * 60 * 1000);
// Get completed batches in period
const completedBatches = Array.from(this.batches.values()).filter(b =>
b.farmId === farmId &&
b.status === 'completed' &&
b.actualHarvestDate &&
new Date(b.actualHarvestDate) >= periodStart
);
const totalYieldKg = completedBatches.reduce((sum, b) => sum + (b.actualYieldKg || 0), 0);
const totalExpectedYield = completedBatches.reduce((sum, b) => sum + b.expectedYieldKg, 0);
// Calculate yield per sqm per year
const growingAreaSqm = farm.specs.growingAreaSqm;
const yearlyMultiplier = 365 / periodDays;
const yieldPerSqmPerYear = growingAreaSqm > 0 ? (totalYieldKg * yearlyMultiplier) / growingAreaSqm : 0;
// Quality breakdown
const gradeACounts = completedBatches.filter(b => b.qualityGrade === 'A').length;
const gradeAPercent = completedBatches.length > 0 ? (gradeACounts / completedBatches.length) * 100 : 0;
// Wastage (difference between expected and actual)
const wastageKg = Math.max(0, totalExpectedYield - totalYieldKg);
const wastagePercent = totalExpectedYield > 0 ? (wastageKg / totalExpectedYield) * 100 : 0;
// Success rate
const allBatches = Array.from(this.batches.values()).filter(b =>
b.farmId === farmId &&
new Date(b.plantingDate) >= periodStart
);
const failedBatches = allBatches.filter(b => b.status === 'failed').length;
const cropSuccessRate = allBatches.length > 0 ? ((allBatches.length - failedBatches) / allBatches.length) * 100 : 100;
// Resource usage
const resourceHistory = this.resourceLogs.get(farmId) || [];
const periodResources = resourceHistory.filter(r =>
new Date(r.periodStart) >= periodStart
);
const totalElectricity = periodResources.reduce((sum, r) => sum + r.electricityKwh, 0);
const totalWater = periodResources.reduce((sum, r) => sum + r.waterUsageL, 0);
const totalCost = periodResources.reduce((sum, r) =>
sum + r.electricityCostUsd + r.waterCostUsd + r.nutrientCostUsd + r.co2CostUsd, 0
);
// Top crops
const cropYields = new Map<string, number>();
const cropRevenue = new Map<string, number>();
for (const batch of completedBatches) {
const currentYield = cropYields.get(batch.cropType) || 0;
cropYields.set(batch.cropType, currentYield + (batch.actualYieldKg || 0));
// Estimate revenue (placeholder - would come from actual sales)
const estimatedRevenue = (batch.actualYieldKg || 0) * 10; // $10/kg placeholder
const currentRevenue = cropRevenue.get(batch.cropType) || 0;
cropRevenue.set(batch.cropType, currentRevenue + estimatedRevenue);
}
const totalRevenue = Array.from(cropRevenue.values()).reduce((a, b) => a + b, 0);
return {
farmId,
generatedAt: now.toISOString(),
period: `${periodDays} days`,
totalYieldKg: Math.round(totalYieldKg * 10) / 10,
yieldPerSqmPerYear: Math.round(yieldPerSqmPerYear * 10) / 10,
cropCyclesCompleted: completedBatches.length,
averageCyclesDays: completedBatches.length > 0
? completedBatches.reduce((sum, b) => {
const start = new Date(b.plantingDate);
const end = new Date(b.actualHarvestDate!);
return sum + (end.getTime() - start.getTime()) / (24 * 60 * 60 * 1000);
}, 0) / completedBatches.length
: 0,
averageQualityScore: completedBatches.length > 0
? completedBatches.reduce((sum, b) => sum + b.healthScore, 0) / completedBatches.length
: 0,
gradeAPercent: Math.round(gradeAPercent),
wastagePercent: Math.round(wastagePercent * 10) / 10,
cropSuccessRate: Math.round(cropSuccessRate),
spaceUtilization: farm.currentCapacityUtilization,
laborHoursPerKg: totalYieldKg > 0 ? 0.5 : 0, // Placeholder
revenueUsd: Math.round(totalRevenue),
costUsd: Math.round(totalCost),
profitMarginPercent: totalRevenue > 0 ? Math.round(((totalRevenue - totalCost) / totalRevenue) * 100) : 0,
revenuePerSqm: growingAreaSqm > 0 ? Math.round((totalRevenue / growingAreaSqm) * yearlyMultiplier) : 0,
carbonFootprintKgPerKg: totalYieldKg > 0 ? 0.3 : 0, // Estimated - very low for VF
waterUseLPerKg: totalYieldKg > 0 ? totalWater / totalYieldKg : 0,
energyUseKwhPerKg: totalYieldKg > 0 ? totalElectricity / totalYieldKg : 0,
topCropsByYield: Array.from(cropYields.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 5)
.map(([crop, yieldKg]) => ({ crop, yieldKg: Math.round(yieldKg * 10) / 10 })),
topCropsByRevenue: Array.from(cropRevenue.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 5)
.map(([crop, revenueUsd]) => ({ crop, revenueUsd: Math.round(revenueUsd) })),
topCropsByEfficiency: Array.from(cropYields.entries())
.map(([crop, yieldKg]) => {
const batches = completedBatches.filter(b => b.cropType === crop);
const avgHealth = batches.reduce((sum, b) => sum + b.healthScore, 0) / batches.length;
return { crop, efficiencyScore: Math.round(avgHealth) };
})
.sort((a, b) => b.efficiencyScore - a.efficiencyScore)
.slice(0, 5)
};
}
/**
* Get all recipes
*/
getRecipes(): GrowingRecipe[] {
return Array.from(this.recipes.values());
}
/**
* Add custom recipe
*/
addRecipe(recipe: GrowingRecipe): void {
this.recipes.set(recipe.id, recipe);
}
/**
* Export state
*/
toJSON(): object {
return {
farms: Array.from(this.farms.entries()),
recipes: Array.from(this.recipes.entries()),
batches: Array.from(this.batches.entries()),
resourceLogs: Array.from(this.resourceLogs.entries())
};
}
/**
* Import state
*/
static fromJSON(data: any): VerticalFarmController {
const controller = new VerticalFarmController();
if (data.farms) {
for (const [key, value] of data.farms) {
controller.farms.set(key, value);
}
}
if (data.recipes) {
for (const [key, value] of data.recipes) {
controller.recipes.set(key, value);
}
}
if (data.batches) {
for (const [key, value] of data.batches) {
controller.batches.set(key, value);
}
}
if (data.resourceLogs) {
for (const [key, value] of data.resourceLogs) {
controller.resourceLogs.set(key, value);
}
}
return controller;
}
}
// Singleton
let controllerInstance: VerticalFarmController | null = null;
export function getVerticalFarmController(): VerticalFarmController {
if (!controllerInstance) {
controllerInstance = new VerticalFarmController();
}
return controllerInstance;
}

View file

@ -0,0 +1,564 @@
/**
* Vertical Farming Types for LocalGreenChain
* Complete vertical farm management, automation, and integration
*/
// Vertical Farm facility
export interface VerticalFarm {
id: string;
name: string;
ownerId: string;
// Location
location: {
latitude: number;
longitude: number;
address: string;
city: string;
country: string;
timezone: string;
};
// Facility specs
specs: FacilitySpecs;
// Growing zones
zones: GrowingZone[];
// Systems
environmentalControl: EnvironmentalControlSystem;
irrigationSystem: IrrigationSystem;
lightingSystem: LightingSystem;
nutrientSystem: NutrientDeliverySystem;
// Automation
automationLevel: 'manual' | 'semi_automated' | 'fully_automated';
automationSystems: AutomationSystem[];
// Status
status: 'offline' | 'starting' | 'operational' | 'maintenance' | 'emergency';
operationalSince: string;
lastMaintenanceDate: string;
// Performance
currentCapacityUtilization: number; // 0-100
averageYieldEfficiency: number; // vs theoretical max
energyEfficiencyScore: number; // 0-100
}
export interface FacilitySpecs {
totalAreaSqm: number;
growingAreaSqm: number;
numberOfLevels: number;
ceilingHeightM: number;
// Capacity
totalGrowingPositions: number;
currentActivePlants: number;
// Infrastructure
powerCapacityKw: number;
waterStorageL: number;
backupPowerHours: number;
// Certifications
certifications: ('organic' | 'gap' | 'haccp' | 'iso_22000' | 'local_food_safety')[];
// Building
buildingType: 'warehouse' | 'greenhouse' | 'container' | 'purpose_built' | 'retrofit';
insulation: 'standard' | 'high_efficiency' | 'passive';
}
export interface GrowingZone {
id: string;
name: string;
level: number;
// Dimensions
areaSqm: number;
lengthM: number;
widthM: number;
// Growing system
growingMethod: 'NFT' | 'DWC' | 'ebb_flow' | 'aeroponics' | 'vertical_towers' | 'rack_system';
plantPositions: number;
// Current status
currentCrop: string;
plantIds: string[];
plantingDate: string;
expectedHarvestDate: string;
// Environment targets
environmentTargets: ZoneEnvironmentTargets;
// Current readings
currentEnvironment: ZoneEnvironmentReadings;
// Status
status: 'empty' | 'preparing' | 'planted' | 'growing' | 'harvesting' | 'cleaning';
}
export interface ZoneEnvironmentTargets {
temperatureC: { min: number; max: number; target: number };
humidityPercent: { min: number; max: number; target: number };
co2Ppm: { min: number; max: number; target: number };
lightPpfd: { min: number; max: number; target: number };
lightHours: number;
nutrientEc: { min: number; max: number; target: number };
nutrientPh: { min: number; max: number; target: number };
waterTempC: { min: number; max: number; target: number };
}
export interface ZoneEnvironmentReadings {
timestamp: string;
// Air
temperatureC: number;
humidityPercent: number;
co2Ppm: number;
vpd: number; // Vapor Pressure Deficit
// Light
ppfd: number;
dli: number; // Daily Light Integral
// Water/Nutrients
waterTempC: number;
ec: number;
ph: number;
dissolvedOxygenPpm: number;
// Air quality
airflowMs: number;
// Status flags
alerts: EnvironmentAlert[];
}
export interface EnvironmentAlert {
parameter: string;
type: 'low' | 'high' | 'critical_low' | 'critical_high' | 'sensor_fault';
value: number;
threshold: number;
timestamp: string;
acknowledged: boolean;
}
// Environmental Control System
export interface EnvironmentalControlSystem {
hvacUnits: HVACUnit[];
co2Injection: CO2System;
humidification: HumidificationSystem;
airCirculation: AirCirculationSystem;
// Control mode
controlMode: 'manual' | 'scheduled' | 'adaptive' | 'ai_optimized';
}
export interface HVACUnit {
id: string;
type: 'cooling' | 'heating' | 'heat_pump';
capacityKw: number;
zones: string[];
status: 'off' | 'running' | 'fault';
currentPowerKw: number;
setpointC: number;
}
export interface CO2System {
type: 'tank' | 'generator' | 'burner';
capacityKg: number;
currentLevelKg: number;
injectionRateKgPerHour: number;
status: 'off' | 'injecting' | 'maintaining' | 'fault';
}
export interface HumidificationSystem {
type: 'misting' | 'ultrasonic' | 'evaporative';
capacityLPerHour: number;
status: 'off' | 'running' | 'fault';
currentOutput: number;
}
export interface AirCirculationSystem {
fans: {
id: string;
zone: string;
type: 'circulation' | 'exhaust' | 'intake';
speedPercent: number;
status: 'off' | 'running' | 'fault';
}[];
}
// Irrigation System
export interface IrrigationSystem {
type: 'recirculating' | 'drain_to_waste';
// Tanks
freshWaterTankL: number;
freshWaterLevelL: number;
nutrientTankL: number;
nutrientLevelL: number;
wasteTankL: number;
wasteLevelL: number;
// Water treatment
waterTreatment: {
ro: boolean;
uv: boolean;
ozone: boolean;
filtration: string;
};
// Pumps
pumps: {
id: string;
type: 'main' | 'zone' | 'recirculation' | 'drain';
status: 'off' | 'running' | 'fault';
flowRateLPerMin: number;
}[];
// Schedule
irrigationSchedule: IrrigationSchedule[];
}
export interface IrrigationSchedule {
zoneId: string;
frequencyPerDay: number;
durationMinutes: number;
times: string[]; // HH:MM format
}
// Lighting System
export interface LightingSystem {
type: 'LED' | 'HPS' | 'fluorescent' | 'hybrid';
// Fixtures
fixtures: LightFixture[];
// Schedules
lightSchedules: LightSchedule[];
// Energy
totalWattage: number;
currentWattage: number;
efficacyUmolJ: number;
}
export interface LightFixture {
id: string;
zoneId: string;
type: 'LED_full_spectrum' | 'LED_red_blue' | 'LED_tunable' | 'HPS' | 'fluorescent';
wattage: number;
ppfdAtCanopy: number;
spectrum: {
red: number;
blue: number;
green: number;
farRed: number;
uv: number;
};
dimmingPercent: number;
status: 'off' | 'on' | 'dimmed' | 'fault';
}
export interface LightSchedule {
zoneId: string;
photoperiod: {
onTime: string; // HH:MM
offTime: string;
totalHours: number;
};
intensity: {
dayPpfd: number;
nightPpfd: number;
rampMinutes: number;
};
spectrum?: {
vegetative: { red: number; blue: number };
flowering: { red: number; blue: number };
};
}
// Nutrient Delivery System
export interface NutrientDeliverySystem {
mixingMethod: 'manual' | 'semi_auto' | 'fully_auto';
// Stock solutions
stockSolutions: StockSolution[];
// Dosing
dosingPumps: DosingPump[];
// Current mix
currentRecipe: NutrientRecipe;
// Monitoring
monitoring: {
ec: number;
ph: number;
lastCalibration: string;
calibrationDue: string;
};
}
export interface StockSolution {
id: string;
name: string;
type: 'nutrient_a' | 'nutrient_b' | 'cal_mag' | 'ph_up' | 'ph_down' | 'silica' | 'beneficial';
concentration: string;
tankSizeL: number;
currentLevelL: number;
reorderThresholdL: number;
}
export interface DosingPump {
id: string;
solutionId: string;
flowRateMlPerMin: number;
status: 'idle' | 'dosing' | 'fault';
totalDosedMl: number;
}
export interface NutrientRecipe {
id: string;
name: string;
cropType: string;
growthStage: 'seedling' | 'vegetative' | 'transition' | 'flowering' | 'fruiting';
// Targets
targetEc: number;
targetPh: number;
// Ratios
ratios: {
n: number;
p: number;
k: number;
ca: number;
mg: number;
s: number;
fe: number;
mn: number;
zn: number;
cu: number;
b: number;
mo: number;
};
// Dosing
dosingRatiosMlPerL: { solutionId: string; mlPerL: number }[];
}
// Automation System
export interface AutomationSystem {
id: string;
name: string;
type: 'planting' | 'transplanting' | 'harvesting' | 'packaging' | 'monitoring' | 'cleaning';
// Hardware
hardware: {
type: 'robotic_arm' | 'conveyor' | 'camera' | 'sensor_array' | 'mobile_robot';
model: string;
status: 'idle' | 'running' | 'maintenance' | 'fault';
};
// Capabilities
capabilities: string[];
throughputPerHour: number;
// Integration
apiEndpoint?: string;
integrationStatus: 'connected' | 'disconnected' | 'error';
}
// Growing Recipe
export interface GrowingRecipe {
id: string;
name: string;
cropType: string;
variety?: string;
version: string;
// Stages
stages: GrowthStage[];
// Expected outcomes
expectedDays: number;
expectedYieldGrams: number;
expectedYieldPerSqm: number;
// Requirements
requirements: {
positions: number;
zoneType: string;
minimumPpfd: number;
idealTemperatureC: number;
};
// Source
source: 'internal' | 'community' | 'commercial';
author?: string;
rating?: number;
timesUsed: number;
}
export interface GrowthStage {
name: string;
daysStart: number;
daysEnd: number;
// Environment
temperature: { day: number; night: number };
humidity: { day: number; night: number };
co2Ppm: number;
lightHours: number;
lightPpfd: number;
// Nutrients
nutrientRecipeId: string;
targetEc: number;
targetPh: number;
// Actions
actions: StageAction[];
}
export interface StageAction {
day: number;
action: 'transplant' | 'thin' | 'train' | 'top' | 'pollinate' | 'harvest_partial';
description: string;
automated: boolean;
}
// Crop Batch for tracking
export interface CropBatch {
id: string;
farmId: string;
zoneId: string;
// Crop info
cropType: string;
variety?: string;
recipeId: string;
// Planting
seedBatchId: string;
plantIds: string[];
plantCount: number;
plantingDate: string;
transplantDate?: string;
// Progress
currentStage: string;
currentDay: number;
healthScore: number;
// Expected
expectedHarvestDate: string;
expectedYieldKg: number;
// Actual (after harvest)
actualHarvestDate?: string;
actualYieldKg?: number;
qualityGrade?: string;
// Status
status: 'germinating' | 'growing' | 'ready' | 'harvesting' | 'completed' | 'failed';
// Issues
issues: BatchIssue[];
// Environmental log
environmentLog: {
timestamp: string;
readings: ZoneEnvironmentReadings;
}[];
}
export interface BatchIssue {
id: string;
timestamp: string;
type: 'pest' | 'disease' | 'nutrient' | 'environment' | 'equipment' | 'other';
severity: 'minor' | 'moderate' | 'severe' | 'critical';
description: string;
affectedPlants: number;
resolution?: string;
resolvedAt?: string;
}
// Energy and Resource Tracking
export interface ResourceUsage {
farmId: string;
periodStart: string;
periodEnd: string;
// Energy
electricityKwh: number;
electricityCostUsd: number;
renewablePercent: number;
peakDemandKw: number;
// Water
waterUsageL: number;
waterCostUsd: number;
waterRecycledPercent: number;
// CO2
co2UsedKg: number;
co2CostUsd: number;
// Nutrients
nutrientsUsedL: number;
nutrientCostUsd: number;
// Comparison
kwhPerKgProduce: number;
litersPerKgProduce: number;
costPerKgProduce: number;
// Benchmarks
industryBenchmarkKwhPerKg: number;
industryBenchmarkLitersPerKg: number;
efficiencyVsBenchmark: number;
}
// Vertical Farm Analytics
export interface FarmAnalytics {
farmId: string;
generatedAt: string;
period: string;
// Production
totalYieldKg: number;
yieldPerSqmPerYear: number;
cropCyclesCompleted: number;
averageCyclesDays: number;
// Quality
averageQualityScore: number;
gradeAPercent: number;
wastagePercent: number;
// Efficiency
cropSuccessRate: number;
spaceUtilization: number;
laborHoursPerKg: number;
// Financial
revenueUsd: number;
costUsd: number;
profitMarginPercent: number;
revenuePerSqm: number;
// Environmental
carbonFootprintKgPerKg: number;
waterUseLPerKg: number;
energyUseKwhPerKg: number;
// Top crops
topCropsByYield: { crop: string; yieldKg: number }[];
topCropsByRevenue: { crop: string; revenueUsd: number }[];
topCropsByEfficiency: { crop: string; efficiencyScore: number }[];
}