apiServer.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. from fastapi import FastAPI, HTTPException
  2. from pydantic import BaseModel
  3. from typing import List, Optional, Dict, Any
  4. from datetime import datetime
  5. import traceback
  6. import numpy as np
  7. import matplotlib.pyplot as plt
  8. from mpl_toolkits.mplot3d import Axes3D
  9. from io import BytesIO
  10. import os
  11. import uvicorn
  12. import sys
  13. root_path = os.getcwd()
  14. sys.path.append(root_path)
  15. plt.rcParams['font.sans-serif'] = ['SimHei']
  16. plt.rcParams['axes.unicode_minus'] = False
  17. app = FastAPI()
  18. # BRDF data item model
  19. class BRDFItem(BaseModel):
  20. sampleModel: str
  21. temperatureK: float
  22. wavelengthUm: str
  23. thetaIncident: float
  24. phiIncident: float
  25. thetaReflected: float
  26. phiReflected: float
  27. measurement1: float
  28. measurement2: float
  29. measurement3: float
  30. measurement4: float
  31. measurement5: float
  32. meanValue: float
  33. repeatabilityPct: float
  34. # Request payload model
  35. class RequestPayload(BaseModel):
  36. filePath: str
  37. brdfList: List[BRDFItem]
  38. # Response model
  39. class ResponseModel(BaseModel):
  40. status: int
  41. msg: str
  42. filePath: Optional[str] = None
  43. # Global exception handler
  44. @app.exception_handler(Exception)
  45. async def global_exception_handler(request, exc):
  46. print(f"Unhandled exception: {str(exc)}")
  47. print(traceback.format_exc())
  48. return {"status": 500, "msg": "Failed"}
  49. def sph2cart(theta: float, phi: float):
  50. x = np.sin(theta) * np.cos(phi)
  51. y = np.sin(theta) * np.sin(phi)
  52. z = np.cos(theta)
  53. return x, y, z
  54. def plot_all_hemispheres(brdf_list: List[BRDFItem], save_path: str):
  55. os.makedirs(os.path.dirname(save_path), exist_ok=True)
  56. # Generate hemisphere surface
  57. phi = np.linspace(0, 2 * np.pi, 60)
  58. theta = np.linspace(0, np.pi / 2, 30)
  59. PHI, THETA = np.meshgrid(phi, theta)
  60. X = np.sin(THETA) * np.cos(PHI)
  61. Y = np.sin(THETA) * np.sin(PHI)
  62. Z = np.cos(THETA)
  63. # Create 3D plot
  64. fig = plt.figure(figsize=(10, 8))
  65. ax = fig.add_subplot(111, projection='3d')
  66. ax.plot_surface(X, Y, Z, rstride=5, cstride=5, color='lightblue', alpha=0.1, edgecolor='k')
  67. # Draw vectors for each BRDF data item
  68. for i, item in enumerate(brdf_list):
  69. # Convert spherical to cartesian coordinates
  70. xi, yi, zi = sph2cart(np.deg2rad(item.thetaIncident), np.deg2rad(item.phiIncident))
  71. xr, yr, zr = sph2cart(np.deg2rad(item.thetaReflected), np.deg2rad(item.phiReflected))
  72. # Assign colors
  73. color_i = plt.cm.tab10(i % 10) # Incident light color
  74. color_r = plt.cm.tab10((i + 5) % 10) # Reflected light color
  75. # Incident light - points inward (toward center)
  76. ax.quiver(xi, yi, zi, -xi, -yi, -zi, color=color_i, linewidth=2, arrow_length_ratio=0.1)
  77. # Reflected light - points outward (away from center)
  78. ax.quiver(0, 0, 0, xr, yr, zr, color=color_r, linewidth=2, arrow_length_ratio=0.1)
  79. # Set labels and limits
  80. ax.set_xlim([-1, 1])
  81. ax.set_ylim([-1, 1])
  82. ax.set_zlim([0, 1])
  83. ax.set_xlabel('sinθ·cosφ')
  84. ax.set_ylabel('sinθ·sinφ')
  85. ax.set_zlabel('Z')
  86. ax.set_title('BRDF扫描轨迹图')
  87. plt.tight_layout()
  88. plt.savefig(save_path, format='png', dpi=300)
  89. plt.close(fig)
  90. buf = BytesIO()
  91. plt.savefig(buf, format='png', dpi=300)
  92. buf.seek(0)
  93. return buf
  94. @app.post("/process_brdf", response_model=ResponseModel)
  95. async def process_brdf(payload: RequestPayload):
  96. """
  97. Process BRDF data endpoint
  98. Parameters:
  99. - filePath: File save path
  100. - brdfList: List of BRDF data items
  101. Returns:
  102. - status: Status code
  103. - msg: Message
  104. - filePath: Saved file path
  105. """
  106. try:
  107. if not payload.filePath:
  108. return {"status": 500, "msg": "File path is empty"}
  109. if not payload.brdfList:
  110. return {"status": 500, "msg": "BRDF list is empty"}
  111. # Plot all BRDF data
  112. buf = plot_all_hemispheres(payload.brdfList, payload.filePath)
  113. return {
  114. "status": 200,
  115. "msg": "Success",
  116. "filePath": payload.filePath
  117. }
  118. except Exception as e:
  119. print(f"Processing error: {str(e)}")
  120. print(traceback.format_exc())
  121. return {"status": 500, "msg": "Failed"}
  122. # if __name__ == "__main__":
  123. # import uvicorn
  124. #
  125. # uvicorn.run(app, host="0.0.0.0", port=8081)
  126. # if __name__ == "__main__":
  127. # uvicorn.run("apiServer:app", host="0.0.0.0", port=8081, reload=False)
  128. if __name__ == "__main__":
  129. # 更可靠的获取模块名方式
  130. name_app = os.path.splitext(os.path.basename(__file__))[0]
  131. # 更完整的日志配置
  132. log_config = {
  133. "version": 1,
  134. "disable_existing_loggers": False,
  135. "formatters": {
  136. "default": {
  137. "()": "uvicorn.logging.DefaultFormatter",
  138. "fmt": "%(levelprefix)s %(asctime)s %(message)s",
  139. "datefmt": "%Y-%m-%d %H:%M:%S",
  140. }
  141. },
  142. "handlers": {
  143. "default": {
  144. "formatter": "default",
  145. "class": "logging.StreamHandler",
  146. "stream": "ext://sys.stderr",
  147. },
  148. "file_handler": {
  149. "class": "logging.FileHandler",
  150. "filename": "logfile.log",
  151. "formatter": "default",
  152. },
  153. },
  154. "root": {
  155. "handlers": ["default", "file_handler"],
  156. "level": "INFO",
  157. },
  158. }
  159. uvicorn.run(
  160. app,
  161. host="0.0.0.0",
  162. port=8081,
  163. reload=False,
  164. log_config=log_config
  165. )