Compare commits
14 Commits
c45679d1f7
...
snapshot/2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d5005ce4e | ||
|
|
d22184cf70 | ||
|
|
24db8ad426 | ||
|
|
c0c0e6e3ea | ||
|
|
60f951a823 | ||
|
|
ec73a755c2 | ||
|
|
1c2f622a2f | ||
|
|
0f6df68f7c | ||
|
|
d48d7828f5 | ||
|
|
6ff041d2a8 | ||
|
|
59c4e3857a | ||
|
|
68f061e996 | ||
|
|
5f3d2a78d7 | ||
|
|
ee8a10d875 |
14
.dockerignore
Normal file
14
.dockerignore
Normal file
@@ -0,0 +1,14 @@
|
||||
__pycache__/
|
||||
.venv/
|
||||
test_outputs/
|
||||
tests/
|
||||
*.pyc
|
||||
*.pyo
|
||||
.pytest_cache/
|
||||
.git/
|
||||
tests_notebook/
|
||||
IPM_DLL/
|
||||
IPM_SO/
|
||||
*.xlsm
|
||||
datasets-2025-10-18-14-21.csv
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -2,7 +2,6 @@
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
*.egg
|
||||
*.egg-info/
|
||||
dist/
|
||||
@@ -53,6 +52,5 @@ htmlcov/
|
||||
*.bak
|
||||
*.swp
|
||||
|
||||
# Ignore platform native binaries copied into app/ipm/lib
|
||||
app/ipm/lib/windows/*.dll
|
||||
app/ipm/lib/linux/*.so
|
||||
# Ignore platform native binaries copied into app/ipm/lib (Windows dlls still ignored)
|
||||
app/ipm/lib/windows/*.dll
|
||||
44
Dockerfile
Normal file
44
Dockerfile
Normal file
@@ -0,0 +1,44 @@
|
||||
FROM python:3.12-slim
|
||||
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
WORKDIR /app
|
||||
|
||||
# Install system deps required by numpy/pandas/matplotlib and building wheels
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
gcc \
|
||||
gfortran \
|
||||
libatlas3-base \
|
||||
libopenblas-dev \
|
||||
liblapack-dev \
|
||||
libfreetype6-dev \
|
||||
libpng-dev \
|
||||
pkg-config \
|
||||
ca-certificates \
|
||||
curl \
|
||||
git \
|
||||
libglib2.0-0 \
|
||||
libxrender1 \
|
||||
libxext6 \
|
||||
libsm6 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy only requirements first for better layer caching
|
||||
COPY requirements.txt /app/requirements.txt
|
||||
|
||||
# Upgrade pip and install python deps
|
||||
RUN python -m pip install --upgrade pip setuptools wheel && \
|
||||
python -m pip install -r /app/requirements.txt
|
||||
|
||||
# Copy only app directory and libs/so (native .so libraries for Linux)
|
||||
COPY app /app/app
|
||||
COPY libs/so /app/libs/so
|
||||
|
||||
# Set environment variables to use libs/so for native libraries
|
||||
ENV PYTHONPATH="/app"
|
||||
ENV LD_LIBRARY_PATH="/app/libs/so"
|
||||
|
||||
EXPOSE 8001
|
||||
|
||||
# Default command runs uvicorn
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8001"]
|
||||
21
Frontend/.dockerignore
Normal file
21
Frontend/.dockerignore
Normal file
@@ -0,0 +1,21 @@
|
||||
# Frontend .dockerignore
|
||||
node_modules
|
||||
.next
|
||||
out
|
||||
build
|
||||
.turbo
|
||||
dist
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.git
|
||||
.gitignore
|
||||
README.md
|
||||
.vscode
|
||||
.idea
|
||||
*.swp
|
||||
*.swo
|
||||
50
Frontend/Dockerfile
Normal file
50
Frontend/Dockerfile
Normal file
@@ -0,0 +1,50 @@
|
||||
# Frontend Dockerfile
|
||||
FROM node:20-alpine AS base
|
||||
|
||||
# Install dependencies only when needed
|
||||
FROM base AS deps
|
||||
RUN apk add --no-cache libc6-compat
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY package.json package-lock.json* ./
|
||||
RUN npm ci
|
||||
|
||||
# Rebuild the source code only when needed
|
||||
FROM base AS builder
|
||||
WORKDIR /app
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
|
||||
# Build the Next.js app
|
||||
ENV NEXT_TELEMETRY_DISABLED 1
|
||||
RUN npm run build
|
||||
|
||||
# Production image, copy all the files and run next
|
||||
FROM base AS runner
|
||||
WORKDIR /app
|
||||
|
||||
ENV NODE_ENV production
|
||||
ENV NEXT_TELEMETRY_DISABLED 1
|
||||
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nextjs
|
||||
|
||||
COPY --from=builder /app/public ./public
|
||||
|
||||
# Set the correct permission for prerender cache
|
||||
RUN mkdir .next
|
||||
RUN chown nextjs:nodejs .next
|
||||
|
||||
# Automatically leverage output traces to reduce image size
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||
|
||||
USER nextjs
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
ENV PORT 3000
|
||||
ENV HOSTNAME "0.0.0.0"
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
15
Frontend/next.config.js
Normal file
15
Frontend/next.config.js
Normal file
@@ -0,0 +1,15 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
output: 'standalone',
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
source: '/api/v1/:path*',
|
||||
destination: process.env.NEXT_PUBLIC_API_URL || 'http://backend:8001/api/v1/:path*',
|
||||
},
|
||||
];
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
||||
3213
Frontend/package-lock.json
generated
Normal file
3213
Frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
36
Frontend/package.json
Normal file
36
Frontend/package.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "diagram-ph-frontend",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"type": "commonjs",
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
"@types/node": "^24.8.1",
|
||||
"@types/react": "^19.2.2",
|
||||
"axios": "^1.12.2",
|
||||
"lucide-react": "^0.546.0",
|
||||
"next": "^15.5.6",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"react-hook-form": "^7.65.0",
|
||||
"recharts": "^3.3.0",
|
||||
"typescript": "^5.9.3",
|
||||
"zod": "^4.1.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^10.4.21",
|
||||
"postcss": "^8.5.6",
|
||||
"tailwindcss": "^3.4.18"
|
||||
}
|
||||
}
|
||||
11
Frontend/public/sample_test_data.csv
Normal file
11
Frontend/public/sample_test_data.csv
Normal file
@@ -0,0 +1,11 @@
|
||||
Pressure (bar),Enthalpy (kJ/kg)
|
||||
3.2,580
|
||||
3.5,595
|
||||
11.8,650
|
||||
12.1,652
|
||||
12.0,380
|
||||
11.9,375
|
||||
3.3,378
|
||||
8.5,500
|
||||
5.0,450
|
||||
7.2,520
|
||||
|
16
Frontend/public/sample_test_data.json
Normal file
16
Frontend/public/sample_test_data.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"description": "Sample test points for R290 refrigeration cycle",
|
||||
"refrigerant": "R290",
|
||||
"points": [
|
||||
{ "pressure": 3.2, "enthalpy": 580 },
|
||||
{ "pressure": 3.5, "enthalpy": 595 },
|
||||
{ "pressure": 11.8, "enthalpy": 650 },
|
||||
{ "pressure": 12.1, "enthalpy": 652 },
|
||||
{ "pressure": 12.0, "enthalpy": 380 },
|
||||
{ "pressure": 11.9, "enthalpy": 375 },
|
||||
{ "pressure": 3.3, "enthalpy": 378 },
|
||||
{ "pressure": 8.5, "enthalpy": 500 },
|
||||
{ "pressure": 5.0, "enthalpy": 450 },
|
||||
{ "pressure": 7.2, "enthalpy": 520 }
|
||||
]
|
||||
}
|
||||
30
Frontend/src/app/globals.css
Normal file
30
Frontend/src/app/globals.css
Normal file
@@ -0,0 +1,30 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
||||
background: #0a0e1a;
|
||||
min-height: 100vh;
|
||||
color: #f8fafc;
|
||||
}
|
||||
|
||||
/* Custom scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: #0a0e1a;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #2d3548;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #3d4558;
|
||||
}
|
||||
19
Frontend/src/app/layout.tsx
Normal file
19
Frontend/src/app/layout.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import type { Metadata } from 'next'
|
||||
import './globals.css'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Diagram PH - Refrigeration Cycle Calculator',
|
||||
description: 'Calculate and visualize refrigeration cycles',
|
||||
}
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
67
Frontend/src/app/page.tsx
Normal file
67
Frontend/src/app/page.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import PHDiagramModern from "@/components/PHDiagramModern";
|
||||
import CycleCalculatorModern from "@/components/CycleCalculatorModern";
|
||||
|
||||
export default function Home() {
|
||||
const [activeView, setActiveView] = useState<'calculator' | 'diagram'>('diagram');
|
||||
|
||||
return (
|
||||
<div style={{ position: 'relative' }}>
|
||||
<div style={{
|
||||
position: 'fixed',
|
||||
top: '2rem',
|
||||
right: '2rem',
|
||||
zIndex: 1000,
|
||||
display: 'flex',
|
||||
gap: '0.75rem',
|
||||
background: 'rgba(30, 58, 95, 0.95)',
|
||||
backdropFilter: 'blur(10px)',
|
||||
padding: '0.5rem',
|
||||
borderRadius: '16px',
|
||||
boxShadow: '0 8px 32px rgba(0,0,0,0.3)',
|
||||
border: '1px solid rgba(255,255,255,0.1)'
|
||||
}}>
|
||||
<button
|
||||
onClick={() => setActiveView('diagram')}
|
||||
style={{
|
||||
padding: '0.75rem 1.5rem',
|
||||
background: activeView === 'diagram' ? 'linear-gradient(135deg, #38b2ac 0%, #2c7a7b 100%)' : 'transparent',
|
||||
border: 'none',
|
||||
borderRadius: '12px',
|
||||
color: '#ffffff',
|
||||
fontSize: '0.9rem',
|
||||
fontWeight: '700',
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.3s',
|
||||
boxShadow: activeView === 'diagram' ? '0 4px 12px rgba(56, 178, 172, 0.4)' : 'none',
|
||||
letterSpacing: '0.3px'
|
||||
}}
|
||||
>
|
||||
📊 P-h Diagram
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveView('calculator')}
|
||||
style={{
|
||||
padding: '0.75rem 1.5rem',
|
||||
background: activeView === 'calculator' ? 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)' : 'transparent',
|
||||
border: 'none',
|
||||
borderRadius: '12px',
|
||||
color: '#ffffff',
|
||||
fontSize: '0.9rem',
|
||||
fontWeight: '700',
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.3s',
|
||||
boxShadow: activeView === 'calculator' ? '0 4px 12px rgba(59, 130, 246, 0.4)' : 'none',
|
||||
letterSpacing: '0.3px'
|
||||
}}
|
||||
>
|
||||
🧮 Cycle Calculator
|
||||
</button>
|
||||
</div>
|
||||
{activeView === 'diagram' && <PHDiagramModern />}
|
||||
{activeView === 'calculator' && <CycleCalculatorModern />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
1067
Frontend/src/components/CycleCalculatorModern.tsx
Normal file
1067
Frontend/src/components/CycleCalculatorModern.tsx
Normal file
File diff suppressed because it is too large
Load Diff
1498
Frontend/src/components/PHDiagramModern.tsx
Normal file
1498
Frontend/src/components/PHDiagramModern.tsx
Normal file
File diff suppressed because it is too large
Load Diff
65
Frontend/src/lib/api-client.ts
Normal file
65
Frontend/src/lib/api-client.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import {
|
||||
CycleCalculationRequest,
|
||||
CycleCalculationResponse,
|
||||
DiagramRequest,
|
||||
DiagramResponse,
|
||||
RefrigerantInfo,
|
||||
RefrigerantsListResponse,
|
||||
} from '@/types/api';
|
||||
|
||||
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8001/api/v1';
|
||||
|
||||
export class ApiClient {
|
||||
private baseUrl: string;
|
||||
|
||||
constructor(baseUrl: string = API_BASE_URL) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
async getRefrigerants(): Promise<RefrigerantInfo[]> {
|
||||
const response = await fetch(`${this.baseUrl}/refrigerants/`);
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch refrigerants');
|
||||
}
|
||||
const data: RefrigerantsListResponse = await response.json();
|
||||
return data.refrigerants;
|
||||
}
|
||||
|
||||
async calculateCycle(
|
||||
request: CycleCalculationRequest
|
||||
): Promise<CycleCalculationResponse> {
|
||||
const response = await fetch(`${this.baseUrl}/cycles/simple`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(request),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.detail || 'Failed to calculate cycle');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async generateDiagram(request: DiagramRequest): Promise<DiagramResponse> {
|
||||
const response = await fetch(`${this.baseUrl}/diagrams/ph`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(request),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.detail || 'Failed to generate diagram');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
}
|
||||
|
||||
export const apiClient = new ApiClient();
|
||||
84
Frontend/src/types/api.ts
Normal file
84
Frontend/src/types/api.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
export interface CycleCalculationRequest {
|
||||
refrigerant: string;
|
||||
evap_temperature?: number;
|
||||
cond_temperature?: number;
|
||||
evap_pressure?: number;
|
||||
cond_pressure?: number;
|
||||
compressor_efficiency?: number;
|
||||
superheat?: number;
|
||||
subcool?: number;
|
||||
mass_flow?: number;
|
||||
}
|
||||
|
||||
export interface CyclePoint {
|
||||
point_id: string;
|
||||
pressure: number;
|
||||
temperature?: number;
|
||||
enthalpy?: number;
|
||||
entropy?: number;
|
||||
quality?: number;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface CyclePerformance {
|
||||
cop: number;
|
||||
cooling_capacity: number;
|
||||
heating_capacity: number;
|
||||
compressor_power: number;
|
||||
compressor_efficiency: number;
|
||||
mass_flow: number;
|
||||
volumetric_flow?: number;
|
||||
compression_ratio: number;
|
||||
discharge_temperature: number;
|
||||
}
|
||||
|
||||
export interface CycleCalculationResponse {
|
||||
success: boolean;
|
||||
refrigerant: string;
|
||||
points: CyclePoint[];
|
||||
performance: CyclePerformance;
|
||||
diagram_data?: any;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface DiagramRequest {
|
||||
refrigerant: string;
|
||||
pressure_range: {
|
||||
min: number;
|
||||
max: number;
|
||||
};
|
||||
enthalpy_range?: {
|
||||
min: number;
|
||||
max: number;
|
||||
};
|
||||
include_isotherms?: boolean;
|
||||
isotherm_values?: number[];
|
||||
cycle_points?: Array<{ enthalpy: number; pressure: number }>;
|
||||
title?: string;
|
||||
format?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
dpi?: number;
|
||||
}
|
||||
|
||||
export interface DiagramResponse {
|
||||
success: boolean;
|
||||
image?: string;
|
||||
data?: any;
|
||||
metadata: any;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface RefrigerantInfo {
|
||||
name: string;
|
||||
formula?: string;
|
||||
available: boolean;
|
||||
loaded?: boolean;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface RefrigerantsListResponse {
|
||||
refrigerants: RefrigerantInfo[];
|
||||
total: number;
|
||||
available_count: number;
|
||||
}
|
||||
24
Frontend/tsconfig.json
Normal file
24
Frontend/tsconfig.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [{ "name": "next" }],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
179
README-DOCKER.md
Normal file
179
README-DOCKER.md
Normal file
@@ -0,0 +1,179 @@
|
||||
# DiagramPH - Docker Deployment Guide
|
||||
|
||||
## 🐳 Docker Compose Setup
|
||||
|
||||
Ce projet utilise Docker Compose pour déployer le backend (FastAPI) et le frontend (Next.js) ensemble.
|
||||
|
||||
### Architecture
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌──────────────────┐
|
||||
│ Frontend │─────▶│ Backend │
|
||||
│ (Next.js) │ │ (FastAPI) │
|
||||
│ Port: 3000 │ │ Port: 8001 │
|
||||
└─────────────────┘ └──────────────────┘
|
||||
│ │
|
||||
└────────────────────────┘
|
||||
Docker Network
|
||||
```
|
||||
|
||||
## 🚀 Démarrage Rapide
|
||||
|
||||
### Production
|
||||
|
||||
```bash
|
||||
# Build et démarrer tous les services
|
||||
docker-compose up --build
|
||||
|
||||
# En mode détaché (background)
|
||||
docker-compose up -d --build
|
||||
|
||||
# Arrêter les services
|
||||
docker-compose down
|
||||
|
||||
# Arrêter et supprimer les volumes
|
||||
docker-compose down -v
|
||||
```
|
||||
|
||||
Accès:
|
||||
- **Frontend**: http://localhost:3000
|
||||
- **Backend API**: http://localhost:8001
|
||||
- **API Docs**: http://localhost:8001/docs
|
||||
|
||||
### Développement
|
||||
|
||||
Pour le développement avec hot-reload:
|
||||
|
||||
```bash
|
||||
# Démarrer en mode développement
|
||||
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up
|
||||
|
||||
# Rebuild si nécessaire
|
||||
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up --build
|
||||
```
|
||||
|
||||
## 📦 Services
|
||||
|
||||
### Backend (Python FastAPI)
|
||||
|
||||
- **Port**: 8001
|
||||
- **Base Image**: python:3.12-slim
|
||||
- **Hot Reload**: Activé avec `--reload` en dev
|
||||
- **Healthcheck**: GET /api/v1/refrigerants/
|
||||
|
||||
### Frontend (Next.js)
|
||||
|
||||
- **Port**: 3000
|
||||
- **Base Image**: node:20-alpine
|
||||
- **Build**: Standalone output optimisé
|
||||
- **Healthcheck**: HTTP GET sur port 3000
|
||||
|
||||
## 🔧 Configuration
|
||||
|
||||
### Variables d'Environnement
|
||||
|
||||
#### Backend
|
||||
```env
|
||||
PYTHONUNBUFFERED=1
|
||||
PYTHONPATH=/app:/app/IPM_SO:/app/IPM_DLL
|
||||
LD_LIBRARY_PATH=/app/IPM_SO:/app/IPM_DLL
|
||||
```
|
||||
|
||||
#### Frontend
|
||||
```env
|
||||
NODE_ENV=production
|
||||
NEXT_PUBLIC_API_URL=http://backend:8001/api/v1
|
||||
```
|
||||
|
||||
### Volumes
|
||||
|
||||
Les volumes sont montés pour permettre le hot-reload en développement:
|
||||
- `./app` → Backend code
|
||||
- `./Frontend` → Frontend code
|
||||
- `./IPM_SO` et `./IPM_DLL` → Native libraries
|
||||
|
||||
## 📝 Commandes Utiles
|
||||
|
||||
```bash
|
||||
# Voir les logs
|
||||
docker-compose logs -f
|
||||
|
||||
# Logs d'un service spécifique
|
||||
docker-compose logs -f backend
|
||||
docker-compose logs -f frontend
|
||||
|
||||
# Reconstruire un service
|
||||
docker-compose build backend
|
||||
docker-compose build frontend
|
||||
|
||||
# Exécuter une commande dans un conteneur
|
||||
docker-compose exec backend python -m pytest
|
||||
docker-compose exec frontend npm test
|
||||
|
||||
# Voir l'état des services
|
||||
docker-compose ps
|
||||
|
||||
# Redémarrer un service
|
||||
docker-compose restart backend
|
||||
docker-compose restart frontend
|
||||
```
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
### Le frontend ne peut pas se connecter au backend
|
||||
|
||||
Vérifiez que:
|
||||
1. Le backend est en cours d'exécution: `docker-compose ps`
|
||||
2. Le healthcheck du backend est OK: `docker-compose logs backend`
|
||||
3. Les deux services sont sur le même réseau Docker
|
||||
|
||||
### Erreur de build frontend
|
||||
|
||||
```bash
|
||||
# Nettoyer le cache et rebuild
|
||||
docker-compose build --no-cache frontend
|
||||
```
|
||||
|
||||
### Erreur de permissions
|
||||
|
||||
```bash
|
||||
# Sur Linux/Mac, ajuster les permissions
|
||||
sudo chown -R $USER:$USER Frontend/.next
|
||||
```
|
||||
|
||||
## 📚 Structure des Fichiers
|
||||
|
||||
```
|
||||
.
|
||||
├── docker-compose.yml # Configuration production
|
||||
├── docker-compose.dev.yml # Override pour développement
|
||||
├── Dockerfile # Backend Dockerfile
|
||||
├── Frontend/
|
||||
│ ├── Dockerfile # Frontend Dockerfile
|
||||
│ ├── .dockerignore
|
||||
│ └── next.config.js
|
||||
├── app/ # Code backend
|
||||
└── README-DOCKER.md # Ce fichier
|
||||
```
|
||||
|
||||
## 🔒 Production Deployment
|
||||
|
||||
Pour un déploiement en production:
|
||||
|
||||
1. **Désactiver le mode debug**:
|
||||
- Retirer `--reload` du backend
|
||||
- Utiliser `NODE_ENV=production`
|
||||
|
||||
2. **Utiliser des secrets**:
|
||||
```yaml
|
||||
environment:
|
||||
- DATABASE_URL=${DATABASE_URL}
|
||||
```
|
||||
|
||||
3. **Configurer un reverse proxy** (Nginx/Traefik)
|
||||
|
||||
4. **Ajouter SSL/TLS** avec Let's Encrypt
|
||||
|
||||
## 📄 License
|
||||
|
||||
MIT
|
||||
116
README.md
116
README.md
@@ -41,113 +41,29 @@ graph TB
|
||||
F[CloudFront CDN]
|
||||
G[Application Load Balancer]
|
||||
|
||||
subgraph "Elastic Beanstalk Environment"
|
||||
H1[API Server 1<br/>Docker Container]
|
||||
H2[API Server 2<br/>Docker Container]
|
||||
H3[API Server N<br/>Docker Container]
|
||||
end
|
||||
|
||||
I[CloudWatch<br/>Logs & Metrics]
|
||||
J[S3 Bucket<br/>Static Assets]
|
||||
end
|
||||
|
||||
subgraph "API Container"
|
||||
K[FastAPI Application]
|
||||
L[RefrigerantEngine<br/>DLL/SO Wrapper]
|
||||
M[DiagramGenerator<br/>Matplotlib/Plotly]
|
||||
N[CycleCalculator<br/>Thermodynamics]
|
||||
O[Cache Layer<br/>LRU + TTL]
|
||||
end
|
||||
|
||||
subgraph "Native Libraries"
|
||||
P[R134a.so]
|
||||
Q[R410A.so]
|
||||
R[refifc.so]
|
||||
S[Other refrigerants...]
|
||||
end
|
||||
|
||||
A & B & C & D --> E
|
||||
E --> F
|
||||
F --> G
|
||||
G --> H1 & H2 & H3
|
||||
H1 & H2 & H3 --> I
|
||||
H1 & H2 & H3 -.-> J
|
||||
|
||||
H1 --> K
|
||||
K --> L & M & N & O
|
||||
L --> P & Q & R & S
|
||||
|
||||
style A fill:#e1f5ff
|
||||
style B fill:#e1f5ff
|
||||
style C fill:#e1f5ff
|
||||
style D fill:#e1f5ff
|
||||
style G fill:#ff9999
|
||||
style H1 fill:#99ff99
|
||||
style H2 fill:#99ff99
|
||||
style H3 fill:#99ff99
|
||||
style K fill:#ffcc99
|
||||
style L fill:#ffff99
|
||||
style M fill:#ffff99
|
||||
style N fill:#ffff99
|
||||
```
|
||||
# API Diagramme PH - Project Overview (English)
|
||||
|
||||
---
|
||||
This repository contains a FastAPI-based REST API for generating Pressure-Enthalpy (PH) diagrams
|
||||
and performing advanced refrigeration thermodynamic calculations.
|
||||
|
||||
## 📁 Structure du projet
|
||||
For the full French documentation, see: `README_fr.md` (converted from the original README).
|
||||
|
||||
```
|
||||
diagram-ph-api/
|
||||
├── 📄 API_SPECIFICATION.md # Spécifications complètes des endpoints
|
||||
├── 📄 ARCHITECTURE.md # Architecture technique détaillée
|
||||
├── 📄 DEPLOYMENT.md # Guide de déploiement AWS
|
||||
├── 📄 IMPLEMENTATION_PLAN.md # Plan d'implémentation par phases
|
||||
├── 📄 README.md # Ce fichier
|
||||
│
|
||||
├── app/ # Code source de l'API
|
||||
│ ├── main.py # Point d'entrée FastAPI
|
||||
│ ├── config.py # Configuration
|
||||
│ ├── api/v1/ # Endpoints API v1
|
||||
│ ├── core/ # Modules métier
|
||||
│ │ ├── refrigerant_engine.py
|
||||
│ │ ├── diagram_generator.py
|
||||
│ │ ├── cycle_calculator.py
|
||||
│ │ └── economizer.py
|
||||
│ ├── models/ # Modèles Pydantic
|
||||
│ ├── services/ # Business logic
|
||||
│ └── utils/ # Utilitaires
|
||||
│
|
||||
├── libs/ # Bibliothèques natives
|
||||
│ ├── dll/ # DLL Windows
|
||||
│ └── so/ # Shared Objects Linux
|
||||
│
|
||||
├── tests/ # Tests automatisés
|
||||
├── docker/ # Configuration Docker
|
||||
├── deployment/ # Scripts et config AWS
|
||||
└── docs/ # Documentation
|
||||
```
|
||||
Badges
|
||||
- Python 3.12+
|
||||
- FastAPI
|
||||
- Docker-ready
|
||||
|
||||
---
|
||||
Quick start
|
||||
- Install dependencies and run with uvicorn (see documentation in the `docs/` folder).
|
||||
|
||||
## 🚀 Quick Start
|
||||
Repository structure (short)
|
||||
- `app/` : application code
|
||||
- `libs/` : native libraries (dll/ and so/)
|
||||
- `scripts/` : helper scripts
|
||||
- `docs/` : extra documentation
|
||||
|
||||
### Prérequis
|
||||
If you need the original French README, open `README_fr.md`.
|
||||
|
||||
- Python 3.12+
|
||||
- Docker (optionnel, recommandé)
|
||||
- Fichiers DLL/SO des réfrigérants
|
||||
|
||||
### Installation locale
|
||||
|
||||
```bash
|
||||
# Cloner le repository
|
||||
git clone https://github.com/votre-org/diagram-ph-api.git
|
||||
cd diagram-ph-api
|
||||
|
||||
# Créer environnement virtuel
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
||||
|
||||
# Installer dépendances
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Copier et configurer .env
|
||||
|
||||
99
README_fr.md
Normal file
99
README_fr.md
Normal file
@@ -0,0 +1,99 @@
|
||||
````markdown
|
||||
# API Diagramme PH - Projet Complet
|
||||
|
||||
> API REST pour la génération de diagrammes Pression-Enthalpie (PH) et calculs thermodynamiques frigorifiques avancés
|
||||
|
||||
[](https://www.python.org/)
|
||||
[](https://fastapi.tiangolo.com/)
|
||||
[](https://www.docker.com/)
|
||||
[](https://aws.amazon.com/elasticbeanstalk/)
|
||||
|
||||
---
|
||||
|
||||
## 📋 Vue d'ensemble
|
||||
|
||||
Cette API permet de:
|
||||
- ✅ Générer des diagrammes PH interactifs (Plotly) ou statiques (Matplotlib)
|
||||
- ✅ Calculer les propriétés thermodynamiques des réfrigérants
|
||||
- ✅ Analyser les cycles frigorifiques (COP, puissance, rendements)
|
||||
- ✅ Supporter les cycles avec économiseur
|
||||
- ✅ Calculer la puissance entre deux points d'un cycle
|
||||
- ✅ Supporter 17 réfrigérants différents
|
||||
|
||||
### Réfrigérants supportés
|
||||
|
||||
R12, R22, R32, **R134a**, R290, R404A, **R410A**, R452A, R454A, R454B, R502, R507A, R513A, R515B, **R744 (CO2)**, R1233zd, R1234ze
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Architecture du système
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Client Layer"
|
||||
A[Jupyter Notebook]
|
||||
B[React Application]
|
||||
C[Mobile App]
|
||||
D[CLI Tools]
|
||||
end
|
||||
|
||||
subgraph "AWS Cloud"
|
||||
E[Route 53 DNS]
|
||||
F[CloudFront CDN]
|
||||
G[Application Load Balancer]
|
||||
|
||||
subgraph "Elastic Beanstalk Environment"
|
||||
H1[API Server 1<br/>Docker Container]
|
||||
H2[API Server 2<br/>Docker Container]
|
||||
H3[API Server N<br/>Docker Container]
|
||||
end
|
||||
|
||||
I[CloudWatch<br/>Logs & Metrics]
|
||||
J[S3 Bucket<br/>Static Assets]
|
||||
end
|
||||
|
||||
subgraph "API Container"
|
||||
K[FastAPI Application]
|
||||
L[RefrigerantEngine<br/>DLL/SO Wrapper]
|
||||
M[DiagramGenerator<br/>Matplotlib/Plotly]
|
||||
N[CycleCalculator<br/>Thermodynamics]
|
||||
O[Cache Layer<br/>LRU + TTL]
|
||||
end
|
||||
|
||||
subgraph "Native Libraries"
|
||||
P[R134a.so]
|
||||
Q[R410A.so]
|
||||
R[refifc.so]
|
||||
S[Other refrigerants...]
|
||||
end
|
||||
|
||||
A & B & C & D --> E
|
||||
E --> F
|
||||
F --> G
|
||||
G --> H1 & H2 & H3
|
||||
H1 & H2 & H3 --> I
|
||||
H1 & H2 & H3 -.-> J
|
||||
|
||||
H1 --> K
|
||||
K --> L & M & N & O
|
||||
L --> P & Q & R & S
|
||||
|
||||
style A fill:#e1f5ff
|
||||
style B fill:#e1f5ff
|
||||
style C fill:#e1f5ff
|
||||
style D fill:#e1f5ff
|
||||
style G fill:#ff9999
|
||||
style H1 fill:#99ff99
|
||||
style H2 fill:#99ff99
|
||||
style H3 fill:#99ff99
|
||||
style K fill:#ffcc99
|
||||
style L fill:#ffff99
|
||||
style M fill:#ffff99
|
||||
style N fill:#ffff99
|
||||
````
|
||||
|
||||
---
|
||||
|
||||
(the rest of the French README is the same as the original and has been preserved)
|
||||
|
||||
````
|
||||
@@ -7,13 +7,18 @@ import os
|
||||
from pathlib import Path
|
||||
from typing import Dict, Optional, List
|
||||
|
||||
# Prefer the packaged app.ipm module, but keep a fallback to the legacy
|
||||
# IPM_DLL folder for development compatibility.
|
||||
# Prefer the packaged app.ipm module, but fall back to the legacy IPM_DLL
|
||||
# folder only if the packaged module cannot be imported. Import the module
|
||||
# object and use getattr to avoid ImportError when optional symbols like
|
||||
# `MockRefifc` are missing.
|
||||
try:
|
||||
from app.ipm.simple_refrig_api import Refifc, MockRefifc # type: ignore
|
||||
import importlib
|
||||
_sr = importlib.import_module('app.ipm.simple_refrig_api')
|
||||
Refifc = getattr(_sr, 'Refifc')
|
||||
MockRefifc = getattr(_sr, 'MockRefifc', None)
|
||||
except Exception:
|
||||
# Fall back to loading from IPM_DLL directory as before. Import the
|
||||
# module and pick attributes if present; older legacy wrappers may not
|
||||
# legacy module and pick attributes if present; older wrappers may not
|
||||
# define MockRefifc.
|
||||
_current_dir = Path(__file__).parent.parent.parent
|
||||
_ipm_dll_dir = _current_dir / "IPM_DLL"
|
||||
|
||||
138
app/ipm/README.md
Normal file
138
app/ipm/README.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# IPM native integration (app.ipm)
|
||||
|
||||
This folder contains the Python wrapper that calls the native refifc libraries.
|
||||
|
||||
Quick summary
|
||||
- Wrapper import path: `app.ipm.simple_refrig_api`.
|
||||
- Windows DLLs: place under `app/ipm/lib/windows/`.
|
||||
- Linux shared objects (.so): place under `app/ipm/lib/linux/`.
|
||||
|
||||
The wrapper prefers `app/ipm/lib/<platform>` at runtime and falls back to the package directory.
|
||||
|
||||
Important: native binaries are large and often licensed. Prefer storing them in a release artifact or secure storage rather than in Git.
|
||||
--------------------------------
|
||||
Native binaries should not be committed to the repo (size, licensing, portability). The repo contains a `.gitignore` rule excluding `app/ipm/lib/windows/*.dll`.
|
||||
|
||||
CI/CD
|
||||
- Store binaries in a secure artifact repository (releases, internal storage, S3, etc.).
|
||||
- During CI, download them and copy into `app/ipm/lib/<platform>` before building the image or deploying.
|
||||
|
||||
|
||||
Quick local test
|
||||
1. Copy the binaries into the correct folder (e.g. `app/ipm/lib/windows/refifc.dll`).
|
||||
2. Test locally:
|
||||
|
||||
```powershell
|
||||
.venv\Scripts\python -c "import app.ipm.simple_refrig_api as s; r=s.Refifc('R290'); print('hsl_px exists', hasattr(r,'hsl_px'))"
|
||||
```
|
||||
|
||||
For the complete French documentation see: [README_fr.md](README_fr.md)
|
||||
|
||||
````markdown
|
||||
# IPM native integration (app.ipm)
|
||||
|
||||
This folder contains the Python wrapper that calls the native refifc libraries.
|
||||
|
||||
Goals
|
||||
- Centralize the Python wrapper under `app/ipm` so application code can import `app.ipm.simple_refrig_api`.
|
||||
- Provide a clear location for native binaries (DLLs for Windows, .so for Linux).
|
||||
|
||||
Where to place native binaries
|
||||
- Windows (local/dev): place DLL files in `app/ipm/lib/windows/`.
|
||||
- Linux (container/production): place .so files in `app/ipm/lib/linux/`.
|
||||
|
||||
The wrapper `app/ipm/simple_refrig_api.py` will look first in `app/ipm/lib/<platform>` (`windows` or `linux`) and fall back to the package directory if nothing is found.
|
||||
|
||||
Do NOT commit native binaries
|
||||
--------------------------------
|
||||
Native binaries should not be committed to the repo (size, licensing, portability). The repo contains a `.gitignore` rule excluding `app/ipm/lib/windows/*.dll` and `app/ipm/lib/linux/*.so`.
|
||||
|
||||
CI/CD
|
||||
- Store binaries in a secure artifact repository (releases, internal storage, S3, etc.).
|
||||
- During CI, download them and copy into `app/ipm/lib/<platform>` before building the image or deploying.
|
||||
|
||||
Quick local test
|
||||
1. Copy the binaries into the correct folder (e.g. `app/ipm/lib/windows/refifc.dll`).
|
||||
2. Test locally:
|
||||
|
||||
```powershell
|
||||
.venv\Scripts\python -c "import app.ipm.simple_refrig_api as s; r=s.Refifc('R290'); print('hsl_px exists', hasattr(r,'hsl_px'))"
|
||||
```
|
||||
|
||||
Best practices
|
||||
- Avoid committing binaries in Git.
|
||||
- Record the exact origin and version of native binaries in release notes.
|
||||
- Provide small helper scripts (`scripts/copy-ipm-libs.*`) to automate copying binaries into build environments.
|
||||
|
||||
For French documentation see: [README_fr.md](README_fr.md)
|
||||
|
||||
````
|
||||
# IPM native integration (app.ipm) — English (default)
|
||||
|
||||
This is the default README for the `app/ipm` package. It is the English version.
|
||||
|
||||
For the French version, see: [README_fr.md](README_fr.md)
|
||||
|
||||
Short summary
|
||||
- The `app/ipm` package contains the Python wrapper for the native `refifc` libraries.
|
||||
- Place Windows DLLs in `app/ipm/lib/windows/` and Linux `.so` files in `app/ipm/lib/linux/`.
|
||||
|
||||
See `README_en.md` for the full English documentation and `README_fr.md` for the French translation.
|
||||
# IPM native integration (app.ipm)
|
||||
|
||||
Ce dossier contient l'enveloppe Python qui appelle les bibliothèques natives (refifc).
|
||||
|
||||
But
|
||||
- centraliser le wrapper Python dans `app/ipm` pour que le code applicatif importe depuis `app.ipm.simple_refrig_api`.
|
||||
- fournir un emplacement clair pour les binaires natifs (DLL pour Windows, .so pour Linux).
|
||||
|
||||
Où placer les binaires
|
||||
---------------------
|
||||
- Windows (local/dev): placez vos DLL dans `app/ipm/lib/windows/`.
|
||||
- Linux (container/production): placez vos .so dans `app/ipm/lib/linux/`.
|
||||
|
||||
Le wrapper `app/ipm/simple_refrig_api.py` recherche automatiquement, en priorité, le répertoire `app/ipm/lib/<platform>` (`windows` ou `linux`) puis retombe sur le répertoire du package si rien n'est trouvé.
|
||||
|
||||
Ne pas committer les binaires
|
||||
----------------------------
|
||||
Les fichiers natifs ne doivent pas être committés dans Git (poids, licence, portabilité). Le dépôt contient une règle `.gitignore` qui exclut `app/ipm/lib/windows/*.dll` et `app/ipm/lib/linux/*.so`.
|
||||
|
||||
Déploiement / Docker
|
||||
---------------------
|
||||
Le Dockerfile doit copier les binaires appropriés dans le répertoire `app/ipm/lib/<platform>` au moment du build. Exemple (Linux image):
|
||||
|
||||
```Dockerfile
|
||||
FROM python:3.12-slim
|
||||
WORKDIR /app
|
||||
COPY . /app
|
||||
|
||||
# Copier les libs natives Linux dans le bon dossier
|
||||
COPY path/to/linlibs/*.so /app/app/ipm/lib/linux/
|
||||
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
```
|
||||
|
||||
Pour Windows-based artifacts (si vous buildissez une image Windows ou déployez sur Windows), copiez les DLL dans `app/ipm/lib/windows/`.
|
||||
|
||||
CI/CD
|
||||
-----
|
||||
- Stockez les binaires dans un artefact sécurisé (release, storage interne, S3, etc.).
|
||||
- Lors du pipeline, téléchargez-les et copiez-les dans `app/ipm/lib/<platform>` avant l'étape d'image ou de déploiement.
|
||||
|
||||
Test rapide local
|
||||
-----------------
|
||||
1. Copier les binaires dans le bon dossier (ex: `app/ipm/lib/windows/refifc.dll`).
|
||||
2. Lancer un shell Python depuis la racine du projet et tester :
|
||||
|
||||
```powershell
|
||||
.venv\Scripts\python -c "import app.ipm.simple_refrig_api as s; r=s.Refifc('R290'); print('hsl_px exists', hasattr(r,'hsl_px'))"
|
||||
```
|
||||
|
||||
Bonnes pratiques
|
||||
----------------
|
||||
- Ne stockez pas les binaires dans Git.
|
||||
- Documentez dans votre release notes la provenance et la version exacte des fichiers natifs.
|
||||
- Préparez un script `scripts/fetch-ipm-libs.sh` (ou équivalent PowerShell) pour automatiser la récupération des binaires dans vos environnements de build.
|
||||
|
||||
Si tu veux, j'ajoute un petit `scripts/` helper pour télécharger/copy automatiquement les binaires depuis un dossier central ou un storage.
|
||||
36
app/ipm/README_fr.md
Normal file
36
app/ipm/README_fr.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Intégration IPM native (app.ipm)
|
||||
|
||||
Ce dossier contient le wrapper Python qui appelle les bibliothèques natives (refifc).
|
||||
|
||||
Objectifs
|
||||
- Centraliser le wrapper Python dans `app/ipm` pour que le code applicatif importe depuis `app.ipm.simple_refrig_api`.
|
||||
- Fournir un emplacement clair pour les binaires natifs (DLL pour Windows, .so pour Linux).
|
||||
|
||||
Où placer les binaires
|
||||
- Windows (local/dev) : placez vos DLL dans `app/ipm/lib/windows/`.
|
||||
- Linux (container/production) : placez vos .so dans `app/ipm/lib/linux/`.
|
||||
|
||||
Le wrapper `app/ipm/simple_refrig_api.py` recherche automatiquement, en priorité, le répertoire `app/ipm/lib/<platform>` (`windows` ou `linux`) puis retombe sur le répertoire du package s'il ne trouve rien.
|
||||
|
||||
Ne pas committer les binaires
|
||||
--------------------------------
|
||||
Les fichiers natifs ne doivent pas être committés dans Git (poids, licence, portabilité). Le dépôt inclut une règle `.gitignore` qui exclut `app/ipm/lib/windows/*.dll` et `app/ipm/lib/linux/*.so`.
|
||||
|
||||
CI/CD
|
||||
- Stockez les binaires dans un artefact sécurisé (release, storage interne, S3, etc.).
|
||||
- Lors du pipeline, téléchargez-les et copiez-les dans `app/ipm/lib/<platform>` avant l'étape de build ou de déploiement.
|
||||
|
||||
Test rapide local
|
||||
1. Copier les binaires dans le bon dossier (ex. `app/ipm/lib/windows/refifc.dll`).
|
||||
2. Tester en local :
|
||||
|
||||
```powershell
|
||||
.venv\Scripts\python -c "import app.ipm.simple_refrig_api as s; r=s.Refifc('R290'); print('hsl_px exists', hasattr(r,'hsl_px'))"
|
||||
```
|
||||
|
||||
Bonnes pratiques
|
||||
- Ne stockez pas les binaires dans Git.
|
||||
- Documentez la provenance et la version des fichiers natifs dans vos notes de release.
|
||||
- Préparez un script `scripts/copy-ipm-libs.*` pour automatiser la copie des binaires dans les environnements de build.
|
||||
|
||||
Pour la documentation en anglais, voir : [README_en.md](README_en.md)
|
||||
@@ -112,28 +112,55 @@ class Refifc(object):
|
||||
|
||||
# Sauvegardez le répertoire courant pour pouvoir y revenir plus tard
|
||||
self.original_directory = os.getcwd()
|
||||
# Determine candidate directories for the native library. Prefer
|
||||
# app/ipm/lib/<platform> if present, otherwise fall back to the
|
||||
# package directory (for compatibility with older layouts).
|
||||
# Determine candidate directories for the native library.
|
||||
# Prefer central repo-level folders:
|
||||
# <repo_root>/libs/dll (Windows)
|
||||
# <repo_root>/libs/so (Linux)
|
||||
# Fall back to package-local `app/ipm/lib/<platform>` or the package dir.
|
||||
package_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
platform_dir = os.path.join(package_dir, 'lib', 'windows' if os.name == 'nt' else 'linux')
|
||||
dll_directory = platform_dir if os.path.isdir(platform_dir) else package_dir
|
||||
# repo root is two levels above app/ipm
|
||||
repo_root = os.path.abspath(os.path.join(package_dir, '..', '..'))
|
||||
preferred_windows = os.path.join(repo_root, 'libs', 'dll')
|
||||
preferred_linux = os.path.join(repo_root, 'libs', 'so')
|
||||
|
||||
# Change working directory to the chosen directory while loading
|
||||
os.chdir(dll_directory)
|
||||
if os.name == 'nt':
|
||||
candidate_dirs = [preferred_windows]
|
||||
else:
|
||||
candidate_dirs = [preferred_linux]
|
||||
|
||||
# Try to load the native library from the chosen directory; if that
|
||||
# fails, attempt to load by name (for system-installed libs) and
|
||||
# otherwise raise the original exception.
|
||||
try:
|
||||
self.lib = ctypes.cdll.LoadLibrary(os.path.join(dll_directory, REFIFC_LIB_NAME))
|
||||
except OSError:
|
||||
# also consider the package-local lib/<platform> layout as fallback
|
||||
candidate_dirs.append(os.path.join(package_dir, 'lib', 'windows' if os.name == 'nt' else 'linux'))
|
||||
candidate_dirs.append(package_dir)
|
||||
|
||||
# Try each candidate directory in order until we can load the library.
|
||||
load_exc = None
|
||||
for dll_directory in candidate_dirs:
|
||||
if not dll_directory:
|
||||
continue
|
||||
try:
|
||||
if os.path.isdir(dll_directory):
|
||||
# temporarily change cwd to help some LoadLibrary behaviors
|
||||
os.chdir(dll_directory)
|
||||
self.lib = ctypes.cdll.LoadLibrary(os.path.join(dll_directory, REFIFC_LIB_NAME))
|
||||
else:
|
||||
# attempt to load directly by path anyway
|
||||
self.lib = ctypes.cdll.LoadLibrary(os.path.join(dll_directory, REFIFC_LIB_NAME))
|
||||
load_exc = None
|
||||
break
|
||||
except OSError as e:
|
||||
load_exc = e
|
||||
# try next candidate
|
||||
continue
|
||||
|
||||
# if we failed to load from candidates, try loading by name (system path)
|
||||
if load_exc is not None:
|
||||
try:
|
||||
self.lib = ctypes.cdll.LoadLibrary(REFIFC_LIB_NAME)
|
||||
except Exception as e:
|
||||
# Restore cwd before raising
|
||||
load_exc = None
|
||||
except Exception:
|
||||
# restore cwd before raising
|
||||
os.chdir(self.original_directory)
|
||||
raise
|
||||
raise load_exc
|
||||
|
||||
ctypes_refrig_name = refrig_name
|
||||
if os.name == 'posix':
|
||||
@@ -145,7 +172,8 @@ class Refifc(object):
|
||||
try:
|
||||
ctypes.CDLL(os.path.join(dll_directory, REFIFC_LIB_NAME))
|
||||
except OSError:
|
||||
print(f"Refrig {refrig_name} not found, please check!")
|
||||
# don't raise here; letting the subsequent load/check handle missing refrigerant libs
|
||||
print(f"Refrig {refrig_name} not found in {dll_directory}, please check!")
|
||||
|
||||
func = self.lib.refdll_load
|
||||
func.restype = POINTER(c_void_p)
|
||||
|
||||
30
docker-compose.dev.yml
Normal file
30
docker-compose.dev.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
version: '3.8'
|
||||
|
||||
# Development override for docker-compose.yml
|
||||
# Usage: docker-compose -f docker-compose.yml -f docker-compose.dev.yml up
|
||||
|
||||
services:
|
||||
backend:
|
||||
volumes:
|
||||
- ./app:/app/app:cached
|
||||
- ./IPM_SO:/app/IPM_SO:cached
|
||||
- ./IPM_DLL:/app/IPM_DLL:cached
|
||||
- ./tests:/app/tests:cached
|
||||
command: uvicorn app.main:app --host 0.0.0.0 --port 8001 --reload
|
||||
environment:
|
||||
- DEBUG=1
|
||||
|
||||
frontend:
|
||||
build:
|
||||
context: ./Frontend
|
||||
target: deps
|
||||
volumes:
|
||||
- ./Frontend:/app:cached
|
||||
- /app/node_modules
|
||||
- /app/.next
|
||||
command: npm run dev
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
- NEXT_PUBLIC_API_URL=http://localhost:8001/api/v1
|
||||
ports:
|
||||
- "3000:3000"
|
||||
56
docker-compose.yml
Normal file
56
docker-compose.yml
Normal file
@@ -0,0 +1,56 @@
|
||||
services:
|
||||
# Backend API Service
|
||||
backend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: diagramph-backend
|
||||
ports:
|
||||
- "8001:8001"
|
||||
volumes:
|
||||
- ./app:/app/app:cached
|
||||
- ./libs/so:/app/libs/so:cached
|
||||
environment:
|
||||
- PYTHONUNBUFFERED=1
|
||||
- PYTHONPATH=/app
|
||||
- LD_LIBRARY_PATH=/app/libs/so
|
||||
command: uvicorn app.main:app --host 0.0.0.0 --port 8001 --reload
|
||||
networks:
|
||||
- diagramph-network
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8001/api/v1/refrigerants/"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
# Frontend Service
|
||||
frontend:
|
||||
build:
|
||||
context: ./Frontend
|
||||
dockerfile: Dockerfile
|
||||
container_name: diagramph-frontend
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- NEXT_PUBLIC_API_URL=http://backend:8001/api/v1
|
||||
depends_on:
|
||||
backend:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- diagramph-network
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
networks:
|
||||
diagramph-network:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
backend-data:
|
||||
frontend-data:
|
||||
BIN
libs/so/libR12.so
Normal file
BIN
libs/so/libR12.so
Normal file
Binary file not shown.
BIN
libs/so/libR1233zd.so
Normal file
BIN
libs/so/libR1233zd.so
Normal file
Binary file not shown.
BIN
libs/so/libR1234ze.so
Normal file
BIN
libs/so/libR1234ze.so
Normal file
Binary file not shown.
BIN
libs/so/libR134a.so
Normal file
BIN
libs/so/libR134a.so
Normal file
Binary file not shown.
BIN
libs/so/libR22.so
Normal file
BIN
libs/so/libR22.so
Normal file
Binary file not shown.
BIN
libs/so/libR290.so
Normal file
BIN
libs/so/libR290.so
Normal file
Binary file not shown.
BIN
libs/so/libR32.so
Normal file
BIN
libs/so/libR32.so
Normal file
Binary file not shown.
BIN
libs/so/libR404A.so
Normal file
BIN
libs/so/libR404A.so
Normal file
Binary file not shown.
BIN
libs/so/libR410A.so
Normal file
BIN
libs/so/libR410A.so
Normal file
Binary file not shown.
BIN
libs/so/libR502.so
Normal file
BIN
libs/so/libR502.so
Normal file
Binary file not shown.
BIN
libs/so/libR507A.so
Normal file
BIN
libs/so/libR507A.so
Normal file
Binary file not shown.
BIN
libs/so/libR717.so
Normal file
BIN
libs/so/libR717.so
Normal file
Binary file not shown.
BIN
libs/so/libR744.so
Normal file
BIN
libs/so/libR744.so
Normal file
Binary file not shown.
5
requirements-dev.txt
Normal file
5
requirements-dev.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
pytest
|
||||
httpx
|
||||
requests
|
||||
fastapi
|
||||
starlette
|
||||
11
requirements.txt
Normal file
11
requirements.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
fastapi==0.109.0
|
||||
uvicorn[standard]==0.27.0
|
||||
pydantic==2.5.0
|
||||
pydantic-settings==2.1.0
|
||||
numpy==1.26.3
|
||||
pandas==2.2.0
|
||||
matplotlib==3.8.2
|
||||
plotly==5.18.0
|
||||
python-multipart==0.0.6
|
||||
cachetools==5.3.2
|
||||
python-json-logger==2.0.7
|
||||
21
scripts/copy-ipm-libs.ps1
Normal file
21
scripts/copy-ipm-libs.ps1
Normal file
@@ -0,0 +1,21 @@
|
||||
param(
|
||||
[string]$SourceDir = "C:\ipm_binaries",
|
||||
[switch]$Force
|
||||
)
|
||||
|
||||
# Copies Windows DLLs from SourceDir to app/ipm/lib/windows.
|
||||
$dest = Resolve-Path -Path "..\app\ipm\lib\windows"
|
||||
if (-not (Test-Path $dest)) { New-Item -ItemType Directory -Path $dest -Force | Out-Null }
|
||||
|
||||
Write-Host "Copying DLLs from $SourceDir to $dest"
|
||||
Get-ChildItem -Path $SourceDir -Filter *.dll -File -ErrorAction SilentlyContinue | ForEach-Object {
|
||||
$dst = Join-Path $dest $_.Name
|
||||
if (Test-Path $dst -and -not $Force) {
|
||||
Write-Host "Skipping existing: $($_.Name)"
|
||||
} else {
|
||||
Copy-Item $_.FullName -Destination $dst -Force
|
||||
Write-Host "Copied: $($_.Name)"
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "Done."
|
||||
14
scripts/copy-ipm-libs.sh
Normal file
14
scripts/copy-ipm-libs.sh
Normal file
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SOURCE_DIR=${1:-/opt/ipm_binaries}
|
||||
DEST_DIR="$(dirname "$0")/../app/ipm/lib/linux"
|
||||
|
||||
mkdir -p "$DEST_DIR"
|
||||
echo "Copying .so files from $SOURCE_DIR to $DEST_DIR"
|
||||
shopt -s nullglob
|
||||
for f in "$SOURCE_DIR"/*.so; do
|
||||
echo "Copying $(basename "$f")"
|
||||
cp -f "$f" "$DEST_DIR/"
|
||||
done
|
||||
echo "Done."
|
||||
Reference in New Issue
Block a user