feat: Integrate `licaeros` library for licensed AEROS API calls, centralizing request logic in `aeros_utils.py` and updating services to use it.

main
Cizz22 3 weeks ago
parent e740b46612
commit d6d6ab3631

@ -16,5 +16,7 @@ COLLECTOR_CREDENTIAL_PASSWORD=postgres
COLLECTOR_NAME=digital_aeros_fixed
AEROS_LICENSE_ID=20260218-Jre5VZieQfWXTq0G8ClpVSGszMf4UEUMLS5ENpWRVcoVSrNJckVZzXE
AEROS_LICENSE_SECRET=GmLIxf9fr8Ap5m1IYzkk4RPBFcm7UBvcd0eRdRQ03oRdxLHQA0d9oyhUk2ZlM3LVdRh1mkgYy5254bmCjFyWWc0oPFwNWYzNwDwnv50qy6SLRdaFnI0yZcfLbWQ7qCSj
WINDOWS_AEROS_BASE_URL=http://192.168.1.102:8800
TEMPORAL_URL=http://192.168.1.86:7233
TEMPORAL_URL=http://192.168.1.86:7233

23
poetry.lock generated

@ -1076,6 +1076,27 @@ MarkupSafe = ">=2.0"
[package.extras]
i18n = ["Babel (>=2.7)"]
[[package]]
name = "licaeros"
version = "0.1.0"
description = ""
optional = false
python-versions = "*"
groups = ["main"]
files = [
{file = "licaeros-0.1.0-cp310-cp310-linux_x86_64.whl", hash = "sha256:7ab820555c10edae6d8f391e71f0188463283ddbf60840a3baadb926bb78a6a9"},
{file = "licaeros-0.1.0-cp311-cp311-linux_x86_64.whl", hash = "sha256:70b6b753c96e6fa4912e0e9eddf8088ddc3c4b6947ca16dc4b19047d2ee4aedf"},
{file = "licaeros-0.1.0-cp312-cp312-linux_x86_64.whl", hash = "sha256:d89fe52a78637f2a72abf64f6a74445aad3899303cce7409f2d12fd277a0db00"},
]
[package.dependencies]
requests = "*"
[package.source]
type = "legacy"
url = "https://git.reliabilityindonesia.com/api/packages/DigitalTwin/pypi/simple"
reference = "licaeros-repo"
[[package]]
name = "limits"
version = "3.13.0"
@ -2823,4 +2844,4 @@ propcache = ">=0.2.1"
[metadata]
lock-version = "2.1"
python-versions = "^3.11"
content-hash = "9d61d2415a02d2e9a0be0850394cd0d8dbf178f7a419e726cc9b3b1c37d8d4d1"
content-hash = "f75002a661bdae021c2906c1b44ba85cfe1835d37378a717ee9561e376603771"

@ -32,8 +32,14 @@ aiohttp = "^3.12.14"
ijson = "^3.4.0"
redis = "^7.1.0"
clamd = "^1.0.2"
licaeros = "^0.1.0"
[[tool.poetry.source]]
name = "licaeros-repo"
url = "https://git.reliabilityindonesia.com/api/packages/DigitalTwin/pypi/simple/"
priority = "supplemental"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

@ -14,7 +14,7 @@ from .service import save_default_equipment, get_all
# from .schema import (OverhaulScheduleCreate, OverhaulSchedulePagination, OverhaulScheduleUpdate)
router = APIRouter()
router = APIRouter()
@router.get("", response_model=StandardResponse[EquipmentPagination])

@ -7,7 +7,8 @@ from sqlalchemy import Delete, Select, func, desc, and_, text
from sqlalchemy.orm import selectinload
from src.auth.service import CurrentUser
from src.config import AEROS_BASE_URL, DEFAULT_PROJECT_NAME, RELIABILITY_SERVICE_API
from src.config import DEFAULT_PROJECT_NAME, RELIABILITY_SERVICE_API
from src.aeros_utils import aeros_post
from src.database.core import CollectorDbSession, DbSession
from src.database.service import search_filter_sort_paginate
from .model import AerosEquipment, AerosEquipmentDetail, MasterEquipment, AerosEquipmentGroup, ReliabilityPredictNonRepairable
@ -21,7 +22,7 @@ import json
import pandas as pd
from src.aeros_simulation.service import get_aeros_schematic_by_name
client = httpx.AsyncClient(timeout=300.0)
# Aeros session is managed in src.aeros_utils
log = logging.getLogger()
async def get_project(*, db_session: DbSession):
@ -47,11 +48,13 @@ async def get_all(*, common):
updateNodeReq = {"projectName": project.project_name , "equipmentNames": reg_nodes}
try:
response = await client.post(
f"{AEROS_BASE_URL}/api/UpdateDisParams/GetUpdatedNodeDistributions",
response = await aeros_post(
"/api/UpdateDisParams/GetUpdatedNodeDistributions",
json=updateNodeReq,
headers={"Content-Type": "application/json"},
)
response.raise_for_status()
res = response.json()
@ -96,8 +99,8 @@ async def get_by_id(*, db_session: DbSession, id: UUID):
}
try:
response = await client.post(
f"{AEROS_BASE_URL}/api/UpdateDisParams/GetUpdatedNodeDistributions",
response = await aeros_post(
"/api/UpdateDisParams/GetUpdatedNodeDistributions",
json=aerosNodeReq,
headers={"Content-Type": "application/json"},
)
@ -124,8 +127,8 @@ async def get_aeros_equipment_by_location_tag(*, location_tag: Union[str, List[s
}
try:
response = await client.post(
f"{AEROS_BASE_URL}/api/UpdateDisParams/GetUpdatedNodeDistributions",
response = await aeros_post(
"/api/UpdateDisParams/GetUpdatedNodeDistributions",
json=aerosNodeReq,
headers={"Content-Type": "application/json"},
)
@ -157,8 +160,8 @@ async def update_node(
try:
response = await client.post(
f"{AEROS_BASE_URL}/api/UpdateDisParams/UpdateEquipmentDistributions",
response = await aeros_post(
"/api/UpdateDisParams/UpdateEquipmentDistributions",
json=updateNodeReq,
headers={"Content-Type": "application/json"},
)
@ -188,8 +191,8 @@ async def save_default_equipment(*, db_session: DbSession, project_name: str):
await db_session.execute(query)
try:
response = await client.post(
f"{AEROS_BASE_URL}/api/UpdateDisParams/GetUpdatedNodeDistributions",
response = await aeros_post(
"/api/UpdateDisParams/GetUpdatedNodeDistributions",
json=updateNodeReq,
headers={"Content-Type": "application/json"},
)

@ -9,7 +9,8 @@ from sqlalchemy.orm import selectinload
from src.aeros_equipment.service import save_default_equipment
from src.aeros_simulation.service import save_default_simulation_node
from src.auth.service import CurrentUser
from src.config import WINDOWS_AEROS_BASE_URL, AEROS_BASE_URL, CLAMAV_HOST, CLAMAV_PORT
from src.config import WINDOWS_AEROS_BASE_URL, CLAMAV_HOST, CLAMAV_PORT
from src.aeros_utils import aeros_post
from src.database.core import DbSession
from src.database.service import search_filter_sort_paginate
from src.utils import sanitize_filename
@ -21,6 +22,8 @@ from .schema import AerosProjectInput, OverhaulScheduleCreate, OverhaulScheduleU
import asyncio
ALLOWED_EXTENSIONS = {".aro"}
MAX_FILE_SIZE = 100 * 1024 * 1024 # 100MB
# client = httpx.AsyncClient(timeout=300.0) # Managed in src.aeros_utils
# We still use a local client for WINDOWS_AEROS_BASE_URL if it's not managed by aeros_utils
client = httpx.AsyncClient(timeout=300.0)
@ -166,9 +169,9 @@ async def import_aro_project(*, db_session: DbSession, aeros_project_in: AerosPr
# Update path to AEROS APP
# Example BODy "C/dsad/dsad.aro"
try:
response = await client.post(
f"{AEROS_BASE_URL}/api/Project/ImportAROFile",
content=f'"{aro_path}"',
response = await aeros_post(
"/api/Project/ImportAROFile",
data=f'"{aro_path}"',
headers={"Content-Type": "application/json"},
)
response.raise_for_status()
@ -232,9 +235,9 @@ async def reset_project(*, db_session: DbSession):
project = await fetch_aro_record(db_session=db_session)
try:
response = await client.post(
f"{AEROS_BASE_URL}/api/Project/ImportAROFile",
content=f'"{project.aro_file_path}"',
response = await aeros_post(
"/api/Project/ImportAROFile",
data=f'"{project.aro_file_path}"',
headers={"Content-Type": "application/json"},
)
response.raise_for_status()

@ -8,11 +8,11 @@ import logging
import httpx
from fastapi import HTTPException, status
import ijson
import requests
from sqlalchemy import delete, desc, select, update, and_
from sqlalchemy.orm import selectinload
from src.config import AEROS_BASE_URL, AEROS_BASE_URL_OLD, DEFAULT_PROJECT_NAME
from src.config import DEFAULT_PROJECT_NAME
from src.aeros_utils import aeros_post
from src.database.core import DbSession
from src.database.service import CommonParameters, search_filter_sort_paginate
from src.utils import save_to_pastebin
@ -32,7 +32,7 @@ from src.aeros_equipment.schema import EquipmentWithCustomParameters
from .schema import SimulationInput, SimulationPlotResult, SimulationRankingParameters
from .utils import calculate_eaf, stream_large_array
client = httpx.AsyncClient(timeout=300.0)
# client = httpx.AsyncClient(timeout=300.0) # Managed in src.aeros_utils
active_simulations = {}
@ -399,11 +399,11 @@ async def execute_simulation(*, db_session: DbSession, simulation_id: Optional[U
try:
if not is_saved:
response = await client.post(
f"{AEROS_BASE_URL}/api/Simulation/RunSimulation",
json=sim_data,
headers={"Content-Type": "application/json"},
)
response = await aeros_post(
"/api/Simulation/RunSimulation",
json=sim_data,
headers={"Content-Type": "application/json"},
)
response.raise_for_status()
result = response.json()
@ -420,17 +420,15 @@ async def execute_simulation(*, db_session: DbSession, simulation_id: Optional[U
print("Simulation started with id: %s", simulation.id)
# if not os.path.exists(tmpfile):
response = requests.post(f"{AEROS_BASE_URL}/api/Simulation/RunSimulation", stream=True, json=sim_data)
# Using aeros_post wrapper which uses LicensedSession
response = await aeros_post(
"/api/Simulation/RunSimulation",
stream=True,
json=sim_data
)
file_obj = response.raw
# response = await client.post(
# f"{AEROS_BASE_URL}/api/Simulation/RunSimulation",
# json=sim_data,
# headers={"Content-Type": "application/json"},
# )
# response.raise_for_status()
# result = response.json()
await save_simulation_result(
db_session=db_session, simulation_id=simulation.id, schematic_name=sim_data["SchematicName"], eq_update=eq_update, file_path=file_obj

@ -17,7 +17,7 @@ from fastapi import HTTPException, status
from src.aeros_simulation.model import AerosSimulationCalcResult, AerosSimulationPlotResult
from src.aeros_simulation.service import get_all_aeros_node, get_or_save_node, get_plant_calc_result, get_simulation_by_id, get_simulation_with_plot_result
from src.aeros_simulation.utils import calculate_eaf, calculate_eaf_konkin
from src.config import AEROS_BASE_URL
from src.aeros_utils import aeros_post
from src.database.core import DbSession
@ -32,17 +32,16 @@ async def execute_simulation(
tmpfile = os.path.join(tempfile.gettempdir(), f"simulation_{simulation_id}.json")
try:
async with httpx.AsyncClient(timeout=None) as client:
async with client.stream(
"POST",
f"{AEROS_BASE_URL}/api/Simulation/RunSimulation",
json=sim_data,
headers={"Content-Type": "application/json"},
) as response:
response.raise_for_status()
with open(tmpfile, "wb") as f:
async for chunk in response.aiter_bytes():
f.write(chunk)
response = await aeros_post(
"/api/Simulation/RunSimulation",
json=sim_data,
headers={"Content-Type": "application/json"},
stream=True
)
response.raise_for_status()
with open(tmpfile, "wb") as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
if not is_saved:
# If not saving to DB, just return parsed JSON

@ -0,0 +1,32 @@
import anyio
from licaeros import LicensedSession, device_fingerprint_hex
from src.config import AEROS_BASE_URL, AEROS_LICENSE_ID, AEROS_LICENSE_SECRET
import logging
log = logging.getLogger(__name__)
# Initialize a global session if possible, or create on demand
_aeros_session = None
def get_aeros_session():
global _aeros_session
if _aeros_session is None:
log.info(f"Initializing LicensedSession with base URL: {AEROS_BASE_URL}")
log.info(f"Encrypted Device ID: {device_fingerprint_hex()}")
_aeros_session = LicensedSession(
api_base=AEROS_BASE_URL,
license_id=AEROS_LICENSE_ID,
license_secret=AEROS_LICENSE_SECRET,
)
return _aeros_session
async def aeros_post(path: str, json: dict = None, **kwargs):
"""
Asynchronous wrapper for LicensedSession.post
"""
session = get_aeros_session()
# LicensedSession might not be async-compatible, so we run it in a thread
response = await anyio.to_thread.run_sync(
lambda: session.post(path, json)
)
return response

@ -96,4 +96,7 @@ TEMPORAL_URL = config("TEMPORAL_URL", default="http://192.168.1.86:7233")
RELIABILITY_SERVICE_API = config("RELIABILITY_SERVICE_API", default="http://192.168.1.82:8000/reliability")
CLAMAV_HOST = config("CLAMAV_HOST", default="192.168.1.82")
CLAMAV_PORT = config("CLAMAV_PORT", cast=int, default=3310)
CLAMAV_PORT = config("CLAMAV_PORT", cast=int, default=3310)
AEROS_LICENSE_ID = config("AEROS_LICENSE_ID", default="")
AEROS_LICENSE_SECRET = config("AEROS_LICENSE_SECRET", default="")
Loading…
Cancel
Save