From ac93368e9ac109f401f7a066813889c05b213b5a Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 22 Nov 2025 18:23:08 +0000 Subject: [PATCH] Add seed-to-seed transport tracking, demand forecasting, and vertical farming systems This comprehensive update implements: Transport Tracking System: - Complete seed-to-seed lifecycle tracking with 9 event types - TransportChain blockchain for immutable transport records - Carbon footprint calculation per transport method - Food miles tracking with Haversine distance calculation - QR code generation for full traceability Demand Forecasting System: - Consumer preference registration and aggregation - Regional demand signal generation - Supply gap identification and market matching - Grower planting recommendations with risk assessment - Seasonal planning integration Vertical Farming Module: - Multi-zone facility management - Environmental control systems (HVAC, CO2, humidity, lighting) - Growing recipes with stage-based environment targets - Crop batch tracking with health scoring - Farm analytics generation Documentation: - Complete docs/ folder structure for Turborepo - Seed-to-seed transport concept documentation - Demand forecasting and seasonal planning guides - System architecture and user blockchain design - Transport API reference - Vertical farming integration guide Agent Report: - AGENT_REPORT.md with 5 parallel agent tasks for continued development - API routes implementation task - UI components task - Vertical farming pages task - Testing suite task - Documentation completion task --- AGENT_REPORT.md | 608 +++++++++++++++++++ docs/README.md | 99 ++++ docs/api/transport-api.md | 476 +++++++++++++++ docs/architecture/system-overview.md | 246 ++++++++ docs/architecture/user-blockchain.md | 353 +++++++++++ docs/concepts/demand-forecasting.md | 356 +++++++++++ docs/concepts/seed-to-seed-transport.md | 277 +++++++++ docs/vertical-farming/README.md | 305 ++++++++++ lib/demand/forecaster.ts | 578 ++++++++++++++++++ lib/demand/types.ts | 379 ++++++++++++ lib/transport/tracker.ts | 516 ++++++++++++++++ lib/transport/types.ts | 431 ++++++++++++++ lib/vertical-farming/controller.ts | 759 ++++++++++++++++++++++++ lib/vertical-farming/types.ts | 564 ++++++++++++++++++ 14 files changed, 5947 insertions(+) create mode 100644 AGENT_REPORT.md create mode 100644 docs/README.md create mode 100644 docs/api/transport-api.md create mode 100644 docs/architecture/system-overview.md create mode 100644 docs/architecture/user-blockchain.md create mode 100644 docs/concepts/demand-forecasting.md create mode 100644 docs/concepts/seed-to-seed-transport.md create mode 100644 docs/vertical-farming/README.md create mode 100644 lib/demand/forecaster.ts create mode 100644 lib/demand/types.ts create mode 100644 lib/transport/tracker.ts create mode 100644 lib/transport/types.ts create mode 100644 lib/vertical-farming/controller.ts create mode 100644 lib/vertical-farming/types.ts diff --git a/AGENT_REPORT.md b/AGENT_REPORT.md new file mode 100644 index 0000000..51092f7 --- /dev/null +++ b/AGENT_REPORT.md @@ -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.* diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..703b12e --- /dev/null +++ b/docs/README.md @@ -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. diff --git a/docs/api/transport-api.md b/docs/api/transport-api.md new file mode 100644 index 0000000..f73cdf9 --- /dev/null +++ b/docs/api/transport-api.md @@ -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 +X-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" + } +} +``` diff --git a/docs/architecture/system-overview.md b/docs/architecture/system-overview.md new file mode 100644 index 0000000..9260e22 --- /dev/null +++ b/docs/architecture/system-overview.md @@ -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 diff --git a/docs/architecture/user-blockchain.md b/docs/architecture/user-blockchain.md new file mode 100644 index 0000000..e605774 --- /dev/null +++ b/docs/architecture/user-blockchain.md @@ -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 diff --git a/docs/concepts/demand-forecasting.md b/docs/concepts/demand-forecasting.md new file mode 100644 index 0000000..8ce8a89 --- /dev/null +++ b/docs/concepts/demand-forecasting.md @@ -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 +``` diff --git a/docs/concepts/seed-to-seed-transport.md b/docs/concepts/seed-to-seed-transport.md new file mode 100644 index 0000000..6618945 --- /dev/null +++ b/docs/concepts/seed-to-seed-transport.md @@ -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 diff --git a/docs/vertical-farming/README.md b/docs/vertical-farming/README.md new file mode 100644 index 0000000..186c26a --- /dev/null +++ b/docs/vertical-farming/README.md @@ -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 diff --git a/lib/demand/forecaster.ts b/lib/demand/forecaster.ts new file mode 100644 index 0000000..4341a91 --- /dev/null +++ b/lib/demand/forecaster.ts @@ -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 = { + '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 = new Map(); + private supplyCommitments: Map = new Map(); + private demandSignals: Map = new Map(); + private marketMatches: Map = 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; + totalWeeklyKg: number; + priorities: number[]; + certifications: Set; + 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(); + + 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(); + + 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; +} diff --git a/lib/demand/types.ts b/lib/demand/types.ts new file mode 100644 index 0000000..8555182 --- /dev/null +++ b/lib/demand/types.ts @@ -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; +} diff --git a/lib/transport/tracker.ts b/lib/transport/tracker.ts new file mode 100644 index 0000000..67d8433 --- /dev/null +++ b/lib/transport/tracker.ts @@ -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; + private plantEvents: Map; + private batchEvents: Map; + + 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; +} diff --git a/lib/transport/types.ts b/lib/transport/types.ts new file mode 100644 index 0000000..2612ebe --- /dev/null +++ b/lib/transport/types.ts @@ -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 = { + 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; + breakdownByEventType: Record; + + 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; +} diff --git a/lib/vertical-farming/controller.ts b/lib/vertical-farming/controller.ts new file mode 100644 index 0000000..59daf07 --- /dev/null +++ b/lib/vertical-farming/controller.ts @@ -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 = new Map(); + private recipes: Map = new Map(); + private batches: Map = new Map(); + private resourceLogs: Map = 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(); + const cropRevenue = new Map(); + + 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; +} diff --git a/lib/vertical-farming/types.ts b/lib/vertical-farming/types.ts new file mode 100644 index 0000000..6916555 --- /dev/null +++ b/lib/vertical-farming/types.ts @@ -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 }[]; +}