Device AI Predictions - Quick Reference Guide
Overview
Adding AI prediction support for Device (water meter) time-series data to the existing TSAI-based prediction system.
Full Design: See device_ai_predictions_design.md
Key Changes Summary
1. ASP.NET API (C# .NET Core)
File: dwd-api-aspnet/DwdApiAspNet/Controllers/DeviceController.cs
New Endpoints:
GET /api/Device/{deviceId}/tag/{tagId}/data
- Returns time-series data for a device tag
- Optional: startTime, endTime filters
- Returns: [{ deviceTagId, unixTime, value }]
GET /api/Device/{deviceId}/tag/{tagId}
- Returns tag metadata
- Returns: { id, name, tagType, unit, deviceId }
2. FastAPI (Python)
File: dwd-api-fastapi/main.py
New Models:
class DeviceTagData(BaseModel):
deviceTagId: int
unixTime: int
value: float
class DeviceTagInfo(BaseModel):
id: int
name: str
tagType: str
unit: Optional[str]
deviceId: int
New Endpoint:
GET /device/{device_id}/tag/{tag_id}/graph-data
- Fetches data from ASP.NET API
- Applies TSAI predictions
- Parameters:
* base_url: ASP.NET API URL
* include_forecast: bool (default: true)
* forecast_percentage: float (0.1-2.0)
* startTime, endTime: optional filters
File: dwd-api-fastapi/config.py
New Config:
DEVICE_CONFIG = {
"supported_tag_types": [
"monthly_consumption",
"quarterly_consumption",
"yearly_consumption"
],
"min_data_points": 3,
"default_forecast_percentage": 0.5
}
3. Frontend (React)
File: dwd-frontend-react/src/components/DeviceChart.jsx
Changes:
- Add
showPredictionsprop - Fetch from FastAPI when predictions enabled
- Fetch from ASP.NET API when predictions disabled
- Style forecast data differently (dashed line, opacity 0.7)
File: dwd-frontend-react/src/components/EsriCustomPopup.jsx
Changes:
- Add
PredictionButtonto Device chart section - Pass
showPredictionsprop toDeviceChart - Use existing
handlePredictionTogglehandler
File: dwd-frontend-react/src/constants.jsx
New Constant:
export const FASTAPI_ENDPOINTS = {
// ... existing endpoints
GetDeviceTagGraphData: '/device/{device_id}/tag/{tag_id}/graph-data'
};
Implementation Steps
Step 1: ASP.NET Backend (2-3 hours)
- Open
DeviceController.cs - Add
GetDeviceTagDatamethod - Add
GetDeviceTagInfomethod - Test with Postman/API testing tool
- Verify data format matches design
Step 2: FastAPI Backend (3-4 hours)
- Open
main.py - Add Pydantic models (
DeviceTagData,DeviceTagInfo) - Add
get_device_tag_graph_dataendpoint - Open
config.py, addDEVICE_CONFIG - Test endpoint with sample data
- Verify predictions are generated correctly
Step 3: Frontend Updates (3-4 hours)
- Open
DeviceChart.jsx- Add
showPredictionsprop - Add conditional fetching logic
- Add forecast data styling
- Add
- Open
EsriCustomPopup.jsx- Add
PredictionButtonto Device section - Pass
showPredictionstoDeviceChart
- Add
- Open
constants.jsx- Add
GetDeviceTagGraphDataendpoint
- Add
- Test in browser with real device data
Step 4: Testing & Polish (2-3 hours)
- End-to-end testing
- Error handling verification
- UI/UX polish
- Performance testing with large datasets
- Documentation updates
Code Snippets
ASP.NET Endpoint Template
[HttpGet("{deviceId}/tag/{tagId}/data")]
public async Task<ActionResult<IEnumerable<object>>> GetDeviceTagData(
int deviceId, int tagId,
[FromQuery] long? startTime, [FromQuery] long? endTime)
{
var query = _context.DeviceTagData
.Where(d => d.DeviceTagId == tagId);
if (startTime.HasValue)
query = query.Where(d => d.UnixTime >= startTime.Value);
if (endTime.HasValue)
query = query.Where(d => d.UnixTime <= endTime.Value);
var data = await query
.OrderBy(d => d.UnixTime)
.Select(d => new {
deviceTagId = d.DeviceTagId,
unixTime = d.UnixTime,
value = d.Value
})
.ToListAsync();
return Ok(data);
}
FastAPI Endpoint Template
@app.get("/device/{device_id}/tag/{tag_id}/graph-data")
async def get_device_tag_graph_data(
device_id: int,
tag_id: int,
base_url: str = Query(...),
include_forecast: bool = Query(default=True),
forecast_percentage: float = Query(default=1.0)):
# Fetch from ASP.NET API
url = f"{base_url}Device/{device_id}/tag/{tag_id}/data"
response = await client.get(url)
tag_data = response.json()
# Transform to prediction format
transformed = [
{"simUnixTime": item["unixTime"], "value": item["value"]}
for item in tag_data
]
# Add predictions
if include_forecast:
transformed = add_forecast_data(transformed, forecast_percentage)
# Return with forecast metadata
return {
"device_id": device_id,
"tag_id": tag_id,
"data": transformed,
"forecast_info": {...}
}
React DeviceChart Update
// In DeviceChart.jsx
const DeviceChart = ({ deviceId, leftTag, rightTag, showPredictions = false }) => {
useEffect(() => {
const fetchData = async () => {
const url = showPredictions
? `${FASTAPI_BASE_URL}device/${deviceId}/tag/${leftTag.id}/graph-data`
: `${API_BASE_URL}Device/${deviceId}/tag/${leftTag.id}/data`;
const params = new URLSearchParams();
if (showPredictions) {
params.append('base_url', API_BASE_URL);
params.append('include_forecast', 'true');
params.append('forecast_percentage', '1.0');
}
const response = await fetch(`${url}?${params}`);
const data = await response.json();
// Process and render...
};
fetchData();
}, [deviceId, leftTag, rightTag, showPredictions]);
// ... rest of component
};
React EsriCustomPopup Update
// In EsriCustomPopup.jsx, around the Device chart section
{selectedDisplay === 1 && entityType === DEVICE && (
<Box>
<Box sx={{ display: 'flex', gap: 2, mb: 2 }}>
<DeviceTagDropdown ... />
<DeviceTagDropdown ... />
{/* ADD THIS: */}
{(selectedLeftDeviceTag?.id || selectedRightDeviceTag?.id) && (
<PredictionButton
onClick={handlePredictionToggle}
showPredictions={showPredictions}
loading={predictionLoading}
/>
)}
</Box>
<DeviceChart
deviceId={entityId}
leftTag={selectedLeftDeviceTag}
rightTag={selectedRightDeviceTag}
showPredictions={showPredictions} {/* ADD THIS */}
/>
</Box>
)}
Testing Checklist
Backend Testing
- ASP.NET endpoint returns data for valid device/tag
- ASP.NET endpoint returns 404 for invalid IDs
- FastAPI endpoint returns predictions
- Predictions match expected format
- Error handling works correctly
Frontend Testing
- Prediction button appears for device charts
- Clicking button toggles predictions on/off
- Chart shows historical data correctly
- Chart shows forecast data with different styling
- Loading states work correctly
- Error states are handled gracefully
Integration Testing
- Full flow: Frontend → FastAPI → ASP.NET → Database
- Test with monthly consumption data
- Test with quarterly consumption data
- Test with small datasets (< 10 points)
- Test with large datasets (> 100 points)
Data Flow Diagram
User clicks "AI Predictions" button
↓
Frontend: showPredictions = true
↓
Frontend: DeviceChart re-renders
↓
Frontend: Fetches from FastAPI
GET /device/123/tag/456/graph-data?base_url=...&include_forecast=true
↓
FastAPI: Receives request
↓
FastAPI: Fetches raw data from ASP.NET API
GET {base_url}/Device/123/tag/456/data
↓
ASP.NET: Queries DeviceTagDatum table
↓
ASP.NET: Returns [{ unixTime, value }, ...]
↓
FastAPI: Transforms to prediction format
[{ simUnixTime, value }, ...]
↓
FastAPI: Calls add_forecast_data()
↓
FastAPI: TSAI model generates predictions
↓
FastAPI: Adds isForecast: true to predictions
↓
FastAPI: Returns combined data
{ data: [...historical, ...forecasted], forecast_info: {...} }
↓
Frontend: Receives data
↓
Frontend: Separates historical vs. forecast data
↓
Frontend: Renders chart with styled predictions
- Historical: solid line
- Forecast: dashed line, 70% opacity
↓
User sees predictions in chart
Troubleshooting
Issue: Predictions not showing
Check:
- Is
showPredictionsprop being passed correctly? - Is FastAPI endpoint returning data?
- Is
isForecastflag set on predicted data? - Check browser console for errors
Issue: 404 from ASP.NET API
Check:
- Does device with that ID exist?
- Does tag with that ID exist?
- Does tag belong to that device?
Issue: Poor prediction quality
Check:
- How many data points are available? (Need minimum 3)
- Is data too sparse? (< 10 points)
- Consider using simpler fallback model
- Check for data anomalies/outliers
Issue: Slow performance
Check:
- How many data points being fetched?
- Consider adding date range filters
- Check database indexing on UnixTime
- Consider caching predictions
API Examples
Example 1: Get Device Tag Data (ASP.NET)
GET http://localhost:5000/api/Device/123/tag/456/data
Response:
[
{ "deviceTagId": 456, "unixTime": 1698278400, "value": 1234.56 },
{ "deviceTagId": 456, "unixTime": 1700956800, "value": 1150.23 },
{ "deviceTagId": 456, "unixTime": 1703635200, "value": 1210.89 }
]
Example 2: Get Device Tag Data with Predictions (FastAPI)
GET http://localhost:8000/device/123/tag/456/graph-data?base_url=http://localhost:5000/api/&include_forecast=true&forecast_percentage=0.5
Response:
{
"device_id": 123,
"tag_id": 456,
"include_forecast": true,
"data": [
{ "deviceTagId": 456, "unixTime": 1698278400, "value": 1234.56, "isForecast": false },
{ "deviceTagId": 456, "unixTime": 1700956800, "value": 1150.23, "isForecast": false },
{ "deviceTagId": 456, "unixTime": 1703635200, "value": 1210.89, "isForecast": false },
{ "deviceTagId": 456, "unixTime": 1706313600, "value": 1185.34, "isForecast": true }
],
"forecast_info": {
"forecast_percentage": 0.5,
"forecast_points_added": 1,
"description": "Forecast adds 50% additional data points using TSAI predictions"
}
}
Key Differences: Device vs. Model Data
| Aspect | Model Data (Tanks, Pipes) | Device Data (Meters) |
|---|---|---|
| Granularity | Hourly/Sub-hourly | Monthly/Quarterly/Yearly |
| Data Points | 100s-1000s per day | 12-48 per year |
| Patterns | Diurnal cycles, operational | Seasonal, billing cycles |
| Prediction Horizon | Hours to days | Months to quarters |
| Data Volume | High | Low |
| Sparsity | Dense | Sparse |
Implication: Device predictions may use simpler models (linear regression) more often due to data sparsity.
Next Steps
- Review this guide and full design document
- Set up development environment
- Start with ASP.NET backend (easiest)
- Move to FastAPI (reuses existing logic)
- Finish with frontend (visual changes)
- Test thoroughly
- Deploy and monitor
Estimated Total Time: 10-14 hours
Support & Resources
- Full Design:
device_ai_predictions_design.md - Current Prediction Code:
dwd-api-fastapi/main.py(lines 532-662) - Example Chart:
dwd-frontend-react/src/components/ForecastChart.jsx - Prediction Button:
dwd-frontend-react/src/components/PredictionButton.jsx - Config:
dwd-api-fastapi/config.py