feat: Implement mock API service with endpoints for system, recipes, process control, hardware, safety, and user management

This commit is contained in:
2025-08-06 22:44:51 +02:00
parent 041c500f9d
commit 29320b8c5d

View File

@@ -0,0 +1,569 @@
import type {
SystemInfo,
SystemStatus,
Recipe,
RecipeCreate,
RecipeUpdate,
RecipeList,
ProcessStatus,
ProcessStartRequest,
ProcessActionResponse,
HardwareStatus,
SafetyStatus,
User,
UserCreate,
UserUpdate,
} from '../types'
// Mock data
const mockSystemInfo: SystemInfo = {
name: 'Chocolate Tempering Machine',
version: '2.0.0-mock',
environment: 'mock',
api_version: '1.0.0',
features: {
recipe_management: true,
process_control: true,
hardware_monitoring: false, // Disabled in mock mode
safety_monitoring: true,
user_management: true,
real_time_monitoring: true,
},
endpoints: {
recipes: '/api/v1/recipes',
process: '/api/v1/process',
hardware: '/api/v1/hardware',
users: '/api/v1/users',
system: '/api/v1/system',
health: '/health',
},
}
const mockSystemStatus: SystemStatus = {
system_status: 'warning', // Warning due to mock mode
process_status: 'idle',
hardware_status: 'disconnected', // No hardware in mock mode
safety_status: 'safe',
active_alarms: 0,
uptime_seconds: 3600,
last_updated: new Date().toISOString(),
}
const mockRecipes: Recipe[] = [
{
id: 1,
name: 'Dark Chocolate 70%',
description: 'Premium dark chocolate tempering recipe',
heating_temp: 50.0,
cooling_temp: 27.0,
working_temp: 31.0,
heating_time: 300,
cooling_time: 180,
working_time: 600,
mixer_speed: 50,
fountain_delay: 30,
pedal_enabled: true,
is_active: true,
created_at: '2024-01-01T00:00:00Z',
updated_at: '2024-01-01T00:00:00Z',
},
{
id: 2,
name: 'Milk Chocolate',
description: 'Creamy milk chocolate tempering recipe',
heating_temp: 45.0,
cooling_temp: 26.0,
working_temp: 29.0,
heating_time: 240,
cooling_time: 150,
working_time: 480,
mixer_speed: 45,
fountain_delay: 25,
pedal_enabled: true,
is_active: true,
created_at: '2024-01-02T00:00:00Z',
updated_at: '2024-01-02T00:00:00Z',
},
{
id: 3,
name: 'White Chocolate',
description: 'Delicate white chocolate tempering recipe',
heating_temp: 43.0,
cooling_temp: 25.0,
working_temp: 28.0,
heating_time: 200,
cooling_time: 120,
working_time: 400,
mixer_speed: 40,
fountain_delay: 20,
pedal_enabled: false,
is_active: true,
created_at: '2024-01-03T00:00:00Z',
updated_at: '2024-01-03T00:00:00Z',
},
]
const mockProcessStatus: ProcessStatus = {
is_running: false,
current_phase: 'idle',
recipe_id: null,
recipe_name: null,
progress_percentage: 0,
phase_time_remaining: 0,
total_time_remaining: 0,
current_temperatures: {
tank_bottom: 22.5,
tank_wall: 22.3,
pump: 22.1,
fountain: 22.0,
},
target_temperatures: {
tank_bottom: 0,
tank_wall: 0,
pump: 0,
fountain: 0,
},
motor_speeds: {
mixer: 0,
pump: 0,
},
session_id: null,
started_at: null,
estimated_completion: null,
error: null,
}
const mockHardwareStatus: HardwareStatus = {
connected: false,
port: '/dev/ttyUSB0',
baud_rate: 9600,
modbus_address: 1,
last_communication: null,
communication_errors: 0,
temperatures: {
tank_bottom: 22.5,
tank_wall: 22.3,
pump: 22.1,
fountain: 22.0,
},
motors: {
mixer: { running: false, speed: 0, current: 0 },
pump: { running: false, speed: 0, current: 0 },
},
inputs: {
pedal: false,
emergency_stop: false,
door_sensor: true,
},
outputs: {
heating_element_1: false,
heating_element_2: false,
alarm: false,
fountain_valve: false,
},
}
const mockSafetyStatus: SafetyStatus = {
emergency_stop_active: false,
door_open: false,
temperature_alarms: [],
motor_alarms: [],
system_alarms: [],
last_safety_check: new Date().toISOString(),
}
const mockUsers: User[] = [
{
id: '1',
username: 'admin',
email: 'admin@example.com',
role: 'admin',
full_name: 'System Administrator',
is_active: true,
created_at: '2024-01-01T00:00:00Z',
last_login: new Date().toISOString(),
},
{
id: '2',
username: 'operator',
email: 'operator@example.com',
role: 'operator',
full_name: 'Machine Operator',
is_active: true,
created_at: '2024-01-01T00:00:00Z',
last_login: '2024-01-15T08:30:00Z',
},
]
// Helper function to simulate API delays
const delay = (ms: number = 500) => new Promise(resolve => setTimeout(resolve, ms))
// Mock API service class
class MockApiService {
private currentUser: User = mockUsers[0]
private recipes: Recipe[] = [...mockRecipes]
private processStatus: ProcessStatus = { ...mockProcessStatus }
private hardwareStatus: HardwareStatus = { ...mockHardwareStatus }
// System endpoints
async getSystemInfo(): Promise<SystemInfo> {
await delay(300)
return mockSystemInfo
}
async getSystemStatus(): Promise<SystemStatus> {
await delay(200)
return mockSystemStatus
}
async getHealthCheck(): Promise<{ status: string }> {
await delay(100)
return { status: 'healthy' }
}
// Recipe endpoints
async getRecipes(params?: {
skip?: number
limit?: number
active_only?: boolean
search?: string
}): Promise<RecipeList> {
await delay(300)
let filteredRecipes = [...this.recipes]
if (params?.active_only) {
filteredRecipes = filteredRecipes.filter(r => r.is_active)
}
if (params?.search) {
const search = params.search.toLowerCase()
filteredRecipes = filteredRecipes.filter(r =>
r.name.toLowerCase().includes(search) ||
r.description.toLowerCase().includes(search)
)
}
const skip = params?.skip || 0
const limit = params?.limit || 10
return {
recipes: filteredRecipes.slice(skip, skip + limit),
total: filteredRecipes.length,
skip,
limit,
}
}
async getRecipe(id: number): Promise<Recipe> {
await delay(200)
const recipe = this.recipes.find(r => r.id === id)
if (!recipe) {
throw new Error(`Recipe with id ${id} not found`)
}
return recipe
}
async createRecipe(recipe: RecipeCreate): Promise<Recipe> {
await delay(500)
const newRecipe: Recipe = {
...recipe,
id: Math.max(...this.recipes.map(r => r.id)) + 1,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
}
this.recipes.push(newRecipe)
return newRecipe
}
async updateRecipe(id: number, recipe: RecipeUpdate): Promise<Recipe> {
await delay(500)
const index = this.recipes.findIndex(r => r.id === id)
if (index === -1) {
throw new Error(`Recipe with id ${id} not found`)
}
this.recipes[index] = {
...this.recipes[index],
...recipe,
updated_at: new Date().toISOString(),
}
return this.recipes[index]
}
async deleteRecipe(id: number): Promise<void> {
await delay(300)
const index = this.recipes.findIndex(r => r.id === id)
if (index === -1) {
throw new Error(`Recipe with id ${id} not found`)
}
this.recipes.splice(index, 1)
}
// Process control endpoints
async getProcessStatus(): Promise<ProcessStatus> {
await delay(200)
// Simulate temperature fluctuations if process is running
if (this.processStatus.is_running) {
const temps = this.processStatus.current_temperatures
temps.tank_bottom += (Math.random() - 0.5) * 0.2
temps.tank_wall += (Math.random() - 0.5) * 0.2
temps.pump += (Math.random() - 0.5) * 0.1
temps.fountain += (Math.random() - 0.5) * 0.1
}
return { ...this.processStatus }
}
async startProcess(request: ProcessStartRequest): Promise<ProcessActionResponse> {
await delay(800)
const recipe = this.recipes.find(r => r.id === request.recipe_id)
if (!recipe) {
throw new Error(`Recipe with id ${request.recipe_id} not found`)
}
this.processStatus = {
...this.processStatus,
is_running: true,
current_phase: 'heating',
recipe_id: recipe.id,
recipe_name: recipe.name,
progress_percentage: 0,
phase_time_remaining: recipe.heating_time,
total_time_remaining: recipe.heating_time + recipe.cooling_time + recipe.working_time,
target_temperatures: {
tank_bottom: recipe.heating_temp,
tank_wall: recipe.heating_temp,
pump: recipe.heating_temp,
fountain: recipe.heating_temp,
},
session_id: `mock-session-${Date.now()}`,
started_at: new Date().toISOString(),
estimated_completion: new Date(Date.now() + (recipe.heating_time + recipe.cooling_time + recipe.working_time) * 1000).toISOString(),
}
return {
success: true,
message: `Started tempering process with recipe: ${recipe.name}`,
session_id: this.processStatus.session_id!,
}
}
async pauseProcess(): Promise<ProcessActionResponse> {
await delay(300)
this.processStatus.current_phase = 'paused'
return {
success: true,
message: 'Process paused successfully',
}
}
async resumeProcess(): Promise<ProcessActionResponse> {
await delay(300)
this.processStatus.current_phase = 'heating' // or whatever phase it was in
return {
success: true,
message: 'Process resumed successfully',
}
}
async stopProcess(): Promise<ProcessActionResponse> {
await delay(500)
this.processStatus = {
...mockProcessStatus,
session_id: null,
}
return {
success: true,
message: 'Process stopped successfully',
}
}
async emergencyStop(): Promise<ProcessActionResponse> {
await delay(100)
this.processStatus = {
...mockProcessStatus,
session_id: null,
error: 'Emergency stop activated',
}
return {
success: true,
message: 'Emergency stop activated',
}
}
// Hardware endpoints
async getHardwareStatus(): Promise<HardwareStatus> {
await delay(300)
return { ...this.hardwareStatus }
}
async getTemperatures(): Promise<any[]> {
await delay(200)
return [
{ sensor: 'tank_bottom', temperature: this.hardwareStatus.temperatures.tank_bottom, timestamp: new Date().toISOString() },
{ sensor: 'tank_wall', temperature: this.hardwareStatus.temperatures.tank_wall, timestamp: new Date().toISOString() },
{ sensor: 'pump', temperature: this.hardwareStatus.temperatures.pump, timestamp: new Date().toISOString() },
{ sensor: 'fountain', temperature: this.hardwareStatus.temperatures.fountain, timestamp: new Date().toISOString() },
]
}
async getMotorStatus(): Promise<any[]> {
await delay(200)
return [
{ motor: 'mixer', ...this.hardwareStatus.motors.mixer },
{ motor: 'pump', ...this.hardwareStatus.motors.pump },
]
}
async setMotorSpeed(motorId: string, speed: number): Promise<void> {
await delay(400)
if (motorId in this.hardwareStatus.motors) {
(this.hardwareStatus.motors as any)[motorId].speed = speed
;(this.hardwareStatus.motors as any)[motorId].running = speed > 0
}
}
// Safety endpoints
async getSafetyStatus(): Promise<SafetyStatus> {
await delay(200)
return mockSafetyStatus
}
async acknowledgeAlarm(alarmId: string): Promise<void> {
await delay(300)
// Mock alarm acknowledgment
}
async clearAlarms(): Promise<void> {
await delay(500)
// Mock alarm clearing
}
// User management endpoints
async getUsers(params?: {
skip?: number
limit?: number
active_only?: boolean
}): Promise<{ users: User[]; total: number }> {
await delay(300)
let filteredUsers = [...mockUsers]
if (params?.active_only) {
filteredUsers = filteredUsers.filter(u => u.is_active)
}
const skip = params?.skip || 0
const limit = params?.limit || 10
return {
users: filteredUsers.slice(skip, skip + limit),
total: filteredUsers.length,
}
}
async getUser(id: string): Promise<User> {
await delay(200)
const user = mockUsers.find(u => u.id === id)
if (!user) {
throw new Error(`User with id ${id} not found`)
}
return user
}
async createUser(user: UserCreate): Promise<User> {
await delay(500)
const newUser: User = {
...user,
id: (mockUsers.length + 1).toString(),
created_at: new Date().toISOString(),
last_login: null,
}
mockUsers.push(newUser)
return newUser
}
async updateUser(id: string, user: UserUpdate): Promise<User> {
await delay(500)
const index = mockUsers.findIndex(u => u.id === id)
if (index === -1) {
throw new Error(`User with id ${id} not found`)
}
mockUsers[index] = {
...mockUsers[index],
...user,
}
return mockUsers[index]
}
async deleteUser(id: string): Promise<void> {
await delay(300)
const index = mockUsers.findIndex(u => u.id === id)
if (index === -1) {
throw new Error(`User with id ${id} not found`)
}
mockUsers.splice(index, 1)
}
// Authentication endpoints
async login(username: string, password: string): Promise<{ access_token: string; token_type: string; user: User }> {
await delay(800)
// Mock authentication - accept any user from mockUsers with password "password"
const user = mockUsers.find(u => u.username === username)
if (!user || password !== 'password') {
throw new Error('Invalid username or password')
}
const token = `mock-token-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
// Store token
localStorage.setItem('auth_token', token)
return {
access_token: token,
token_type: 'bearer',
user,
}
}
async logout(): Promise<void> {
await delay(200)
localStorage.removeItem('auth_token')
}
async refreshToken(): Promise<{ access_token: string }> {
await delay(300)
const token = `mock-token-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
localStorage.setItem('auth_token', token)
return { access_token: token }
}
// Data export endpoints
async exportProcessData(sessionId: string, format: 'csv' | 'json' = 'csv'): Promise<Blob> {
await delay(1000)
const mockData = format === 'csv'
? 'timestamp,temperature,phase\n2024-01-01T00:00:00Z,25.0,heating\n2024-01-01T00:01:00Z,26.0,heating'
: JSON.stringify({ session: sessionId, data: [] })
return new Blob([mockData], {
type: format === 'csv' ? 'text/csv' : 'application/json'
})
}
async getProcessMetrics(days: number = 30): Promise<any> {
await delay(500)
return {
total_sessions: 42,
successful_sessions: 38,
average_duration: 3600,
temperature_stability: 98.5,
efficiency_rating: 94.2,
}
}
}
export const mockApi = new MockApiService()