Add API integration for sensors and rooms management
Integrate sensorsApi and roomsApi services into energy store. Add API state, loading/error handling, and async functions for fetching sensor and room data. Update room loading logic to fetch from API. Expose new API functions for analytics and health endpoints. Update SensorManagementView to use localhost WebSocket for real-time updates.
This commit is contained in:
@@ -1,5 +1,18 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, reactive } from 'vue'
|
||||
import {
|
||||
sensorsApi,
|
||||
roomsApi,
|
||||
analyticsApi,
|
||||
healthApi,
|
||||
type SensorInfo as ApiSensorInfo,
|
||||
type RoomInfo as ApiRoomInfo,
|
||||
type AnalyticsSummary,
|
||||
type EnergyTrends,
|
||||
type RoomComparison,
|
||||
type SystemStatus,
|
||||
type HealthCheck
|
||||
} from '@/services'
|
||||
|
||||
const MAX_DATA_POINTS = 100 // Keep the last 100 data points for the chart
|
||||
|
||||
@@ -96,29 +109,46 @@ export const useEnergyStore = defineStore('energy', () => {
|
||||
const roomsData = reactive<Map<string, RoomMetrics>>(new Map())
|
||||
const latestReadings = reactive<Map<string, SensorReading>>(new Map())
|
||||
const sensorDevices = reactive<Map<string, SensorDevice>>(new Map())
|
||||
const availableRooms = ref<string[]>([
|
||||
'Conference Room A',
|
||||
'Conference Room B',
|
||||
'Office Floor 1',
|
||||
'Office Floor 2',
|
||||
'Kitchen',
|
||||
'Lobby',
|
||||
'Server Room',
|
||||
'Storage Room',
|
||||
'Meeting Room 1',
|
||||
'Meeting Room 2'
|
||||
])
|
||||
const availableRooms = ref<string[]>([])
|
||||
const roomsLoading = ref<boolean>(false)
|
||||
const roomsLoaded = ref<boolean>(false)
|
||||
|
||||
// API integration state
|
||||
const apiSensors = ref<ApiSensorInfo[]>([])
|
||||
const apiRooms = ref<ApiRoomInfo[]>([])
|
||||
const analyticsData = ref<{
|
||||
summary: AnalyticsSummary | null
|
||||
trends: EnergyTrends | null
|
||||
roomComparison: RoomComparison | null
|
||||
}>({
|
||||
summary: null,
|
||||
trends: null,
|
||||
roomComparison: null
|
||||
})
|
||||
const systemStatus = ref<SystemStatus | null>(null)
|
||||
const healthStatus = ref<HealthCheck | null>(null)
|
||||
const apiLoading = ref(false)
|
||||
const apiError = ref<string | null>(null)
|
||||
|
||||
let socket: WebSocket | null = null
|
||||
const newDataBuffer: (LegacyEnergyData | SensorReading)[] = []
|
||||
|
||||
// Actions
|
||||
function connect(url: string) {
|
||||
if (isConnected.value) {
|
||||
if (isConnected.value && socket) {
|
||||
console.log('Already connected.')
|
||||
return
|
||||
}
|
||||
|
||||
// Close any existing connection first
|
||||
if (socket) {
|
||||
socket.onclose = null
|
||||
socket.onerror = null
|
||||
socket.onmessage = null
|
||||
socket.close()
|
||||
socket = null
|
||||
}
|
||||
|
||||
console.log(`Connecting to WebSocket at ${url}`)
|
||||
socket = new WebSocket(url)
|
||||
|
||||
@@ -130,14 +160,35 @@ export const useEnergyStore = defineStore('energy', () => {
|
||||
socket.onmessage = (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data)
|
||||
|
||||
// Handle proxy info message from API Gateway
|
||||
if (data.type === 'proxy_info' && data.sensor_service_url) {
|
||||
console.log('Received proxy info, reconnecting to sensor service...')
|
||||
// Close current connection gracefully
|
||||
if (socket) {
|
||||
socket.onclose = null // Prevent triggering disconnect handlers
|
||||
socket.close()
|
||||
socket = null
|
||||
}
|
||||
// Set disconnected state temporarily
|
||||
isConnected.value = false
|
||||
|
||||
// Connect directly to sensor service after a short delay
|
||||
setTimeout(() => {
|
||||
console.log('Connecting directly to sensor service at ws://localhost:8007/ws')
|
||||
connect('ws://localhost:8007/ws')
|
||||
}, 100)
|
||||
return
|
||||
}
|
||||
|
||||
newDataBuffer.push(data)
|
||||
} catch (error) {
|
||||
console.error('Error parsing incoming data:', error)
|
||||
}
|
||||
}
|
||||
|
||||
socket.onclose = () => {
|
||||
console.log('WebSocket connection closed.')
|
||||
socket.onclose = (event) => {
|
||||
console.log(`WebSocket connection closed. Code: ${event.code}, Reason: ${event.reason}`)
|
||||
isConnected.value = false
|
||||
socket = null
|
||||
}
|
||||
@@ -145,7 +196,9 @@ export const useEnergyStore = defineStore('energy', () => {
|
||||
socket.onerror = (error) => {
|
||||
console.error('WebSocket error:', error)
|
||||
isConnected.value = false
|
||||
socket = null
|
||||
if (socket) {
|
||||
socket = null
|
||||
}
|
||||
}
|
||||
|
||||
// Process the buffer at intervals
|
||||
@@ -153,11 +206,17 @@ export const useEnergyStore = defineStore('energy', () => {
|
||||
if (newDataBuffer.length > 0) {
|
||||
const data = newDataBuffer.shift() // Get the oldest data point
|
||||
if (data) {
|
||||
// Skip non-data messages (connection establishment, proxy info, etc.)
|
||||
if (data.type && (data.type === 'connection_established' || data.type === 'proxy_info')) {
|
||||
console.log('Received system message:', data.type)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle both legacy and new data formats
|
||||
if (isLegacyData(data)) {
|
||||
latestMessage.value = data
|
||||
updateSensorData(data)
|
||||
|
||||
|
||||
// Update time series for chart
|
||||
const newLabel = new Date(data.timestamp * 1000).toLocaleTimeString()
|
||||
timeSeriesData.labels.push(newLabel)
|
||||
@@ -165,7 +224,7 @@ export const useEnergyStore = defineStore('energy', () => {
|
||||
} else {
|
||||
// Handle new multi-metric data
|
||||
updateRoomData(data)
|
||||
|
||||
|
||||
// Update time series for chart (use energy values)
|
||||
const newLabel = new Date(data.timestamp * 1000).toLocaleTimeString()
|
||||
timeSeriesData.labels.push(newLabel)
|
||||
@@ -426,18 +485,67 @@ export const useEnergyStore = defineStore('energy', () => {
|
||||
}
|
||||
|
||||
// Room management functions
|
||||
const loadRoomsFromAPI = async (): Promise<void> => {
|
||||
if (roomsLoading.value || roomsLoaded.value) {
|
||||
return // Already loading or loaded
|
||||
}
|
||||
|
||||
roomsLoading.value = true
|
||||
|
||||
try {
|
||||
// Try to load from microservices API first
|
||||
const response = await fetch('/api/v1/rooms/names')
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
if (data.rooms && Array.isArray(data.rooms)) {
|
||||
availableRooms.value = data.rooms.sort()
|
||||
roomsLoaded.value = true
|
||||
console.log('Loaded rooms from microservices API:', data.rooms.length)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: try direct sensor service connection
|
||||
const directResponse = await fetch('http://localhost:8007/rooms/names')
|
||||
|
||||
if (directResponse.ok) {
|
||||
const data = await directResponse.json()
|
||||
if (data.rooms && Array.isArray(data.rooms)) {
|
||||
availableRooms.value = data.rooms.sort()
|
||||
roomsLoaded.value = true
|
||||
console.log('Loaded rooms from sensor service:', data.rooms.length)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// If both fail, use empty list and log warning
|
||||
console.warn('Failed to load rooms from API, starting with empty list')
|
||||
availableRooms.value = []
|
||||
roomsLoaded.value = true
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error loading rooms:', error)
|
||||
// Start with empty list on error
|
||||
availableRooms.value = []
|
||||
roomsLoaded.value = true
|
||||
} finally {
|
||||
roomsLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function addRoom(roomName: string): boolean {
|
||||
if (!roomName.trim()) return false
|
||||
|
||||
|
||||
// Check if room already exists
|
||||
if (availableRooms.value.includes(roomName.trim())) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
// Add room to available rooms list
|
||||
availableRooms.value.push(roomName.trim())
|
||||
availableRooms.value.sort() // Keep rooms sorted alphabetically
|
||||
|
||||
|
||||
console.log(`Added new room: ${roomName}`)
|
||||
return true
|
||||
}
|
||||
@@ -487,10 +595,178 @@ export const useEnergyStore = defineStore('energy', () => {
|
||||
}))
|
||||
}
|
||||
|
||||
// API Integration Functions
|
||||
async function handleApiCall<T>(apiCall: () => Promise<T>): Promise<T | null> {
|
||||
apiLoading.value = true
|
||||
apiError.value = null
|
||||
|
||||
try {
|
||||
const result = await apiCall()
|
||||
return result
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
|
||||
// Handle authentication errors
|
||||
if (errorMessage.includes('401') || errorMessage.includes('Authorization')) {
|
||||
console.warn('Authentication error detected, attempting to re-authenticate...')
|
||||
|
||||
// Try to get fresh auth token
|
||||
try {
|
||||
const authStore = (window as any).__AUTH_STORE__
|
||||
if (authStore && typeof authStore.ensureAuthenticated === 'function') {
|
||||
const authSuccess = await authStore.ensureAuthenticated()
|
||||
if (authSuccess) {
|
||||
console.log('Re-authentication successful, retrying API call...')
|
||||
// Retry the original API call
|
||||
try {
|
||||
const retryResult = await apiCall()
|
||||
return retryResult
|
||||
} catch (retryError) {
|
||||
const retryErrorMessage = retryError instanceof Error ? retryError.message : 'Retry failed'
|
||||
apiError.value = retryErrorMessage
|
||||
console.error('API retry failed:', retryErrorMessage)
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (authError) {
|
||||
console.error('Re-authentication failed:', authError)
|
||||
}
|
||||
}
|
||||
|
||||
apiError.value = errorMessage
|
||||
console.error('API call failed:', errorMessage)
|
||||
return null
|
||||
} finally {
|
||||
apiLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Sensors API functions
|
||||
async function fetchApiSensors(params?: { room?: string; sensor_type?: any; status?: any }) {
|
||||
const result = await handleApiCall(() => sensorsApi.getSensors(params))
|
||||
if (result) {
|
||||
apiSensors.value = result
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
async function fetchApiSensorData(
|
||||
sensorId: string,
|
||||
params?: { start_time?: number; end_time?: number; limit?: number; offset?: number }
|
||||
) {
|
||||
return handleApiCall(() => sensorsApi.getSensorData(sensorId, params))
|
||||
}
|
||||
|
||||
async function updateApiSensorMetadata(sensorId: string, metadata: Record<string, any>) {
|
||||
return handleApiCall(() => sensorsApi.updateSensorMetadata(sensorId, metadata))
|
||||
}
|
||||
|
||||
async function deleteApiSensor(sensorId: string) {
|
||||
return handleApiCall(() => sensorsApi.deleteSensor(sensorId))
|
||||
}
|
||||
|
||||
async function exportApiData(params: {
|
||||
start_time: number
|
||||
end_time: number
|
||||
sensor_ids?: string
|
||||
format?: 'json' | 'csv'
|
||||
}) {
|
||||
return handleApiCall(() => sensorsApi.exportData(params))
|
||||
}
|
||||
|
||||
// Rooms API functions
|
||||
async function fetchApiRooms() {
|
||||
const result = await handleApiCall(() => roomsApi.getRooms())
|
||||
if (result) {
|
||||
apiRooms.value = result
|
||||
// Update available rooms from API data
|
||||
const roomNames = result.map(room => room.room).filter(name => name)
|
||||
if (roomNames.length > 0) {
|
||||
availableRooms.value = [...new Set([...availableRooms.value, ...roomNames])].sort()
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
async function fetchApiRoomData(
|
||||
roomName: string,
|
||||
params?: { start_time?: number; end_time?: number; limit?: number }
|
||||
) {
|
||||
return handleApiCall(() => roomsApi.getRoomData(roomName, params))
|
||||
}
|
||||
|
||||
// Analytics API functions
|
||||
async function fetchAnalyticsSummary(hours: number = 24) {
|
||||
const result = await handleApiCall(() => analyticsApi.getAnalyticsSummary(hours))
|
||||
if (result) {
|
||||
analyticsData.value.summary = result
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
async function fetchEnergyTrends(hours: number = 168) {
|
||||
const result = await handleApiCall(() => analyticsApi.getEnergyTrends(hours))
|
||||
if (result) {
|
||||
analyticsData.value.trends = result
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
async function fetchRoomComparison(hours: number = 24) {
|
||||
const result = await handleApiCall(() => analyticsApi.getRoomComparison(hours))
|
||||
if (result) {
|
||||
analyticsData.value.roomComparison = result
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
async function fetchSystemEvents(params?: {
|
||||
severity?: string
|
||||
event_type?: string
|
||||
hours?: number
|
||||
limit?: number
|
||||
}) {
|
||||
return handleApiCall(() => analyticsApi.getEvents(params))
|
||||
}
|
||||
|
||||
// Health API functions
|
||||
async function fetchSystemStatus() {
|
||||
const result = await handleApiCall(() => healthApi.getStatus())
|
||||
if (result) {
|
||||
systemStatus.value = result
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
async function fetchHealthStatus() {
|
||||
const result = await handleApiCall(() => healthApi.getHealth())
|
||||
if (result) {
|
||||
healthStatus.value = result
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Initialize data from APIs
|
||||
async function initializeFromApi() {
|
||||
await Promise.allSettled([
|
||||
loadRoomsFromAPI(), // Load room names first
|
||||
fetchApiSensors(),
|
||||
fetchApiRooms(),
|
||||
fetchAnalyticsSummary(),
|
||||
fetchSystemStatus(),
|
||||
fetchHealthStatus()
|
||||
])
|
||||
}
|
||||
|
||||
// Initialize mock sensors on store creation
|
||||
initializeMockSensors()
|
||||
|
||||
// Load rooms from API on store initialization
|
||||
loadRoomsFromAPI()
|
||||
|
||||
return {
|
||||
// WebSocket state
|
||||
isConnected,
|
||||
latestMessage,
|
||||
timeSeriesData,
|
||||
@@ -499,6 +775,19 @@ export const useEnergyStore = defineStore('energy', () => {
|
||||
latestReadings,
|
||||
sensorDevices,
|
||||
availableRooms,
|
||||
roomsLoading,
|
||||
roomsLoaded,
|
||||
|
||||
// API state
|
||||
apiSensors,
|
||||
apiRooms,
|
||||
analyticsData,
|
||||
systemStatus,
|
||||
healthStatus,
|
||||
apiLoading,
|
||||
apiError,
|
||||
|
||||
// WebSocket functions
|
||||
connect,
|
||||
disconnect,
|
||||
getCO2Status,
|
||||
@@ -506,9 +795,26 @@ export const useEnergyStore = defineStore('energy', () => {
|
||||
executeSensorAction,
|
||||
getSensorsByRoom,
|
||||
getSensorsByType,
|
||||
loadRoomsFromAPI,
|
||||
addRoom,
|
||||
removeRoom,
|
||||
getRoomStats,
|
||||
getAllRoomsWithStats
|
||||
getAllRoomsWithStats,
|
||||
|
||||
// API functions
|
||||
fetchApiSensors,
|
||||
fetchApiSensorData,
|
||||
updateApiSensorMetadata,
|
||||
deleteApiSensor,
|
||||
exportApiData,
|
||||
fetchApiRooms,
|
||||
fetchApiRoomData,
|
||||
fetchAnalyticsSummary,
|
||||
fetchEnergyTrends,
|
||||
fetchRoomComparison,
|
||||
fetchSystemEvents,
|
||||
fetchSystemStatus,
|
||||
fetchHealthStatus,
|
||||
initializeFromApi
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user