API 구조 이해하기¶

코딩하기 전에 사용할 주요 엔드포인트를 살펴보겠습니다:

1. 모든작업 가져오기¶

GET /api/v6.8/jobs

해당 머신에서 실행 중인 모든 작업의 목록을 반환하며, 시작 시간, 종료 시간, 상태 등의 기본 정보를 포함합니다.

2. 구체적인 직무세부사항 확인하기¶

GET /api/v6.8/jobs/{jobId}

특정 작업에 대한 상세 정보를 반환하며, 다음을 포함합니다:

  • 시작시간: 작업이 시작되었을 때
  • 시간 종료됨작업이 완료되었을 때 (아직 실행 중이면 null)
  • 결과: 직무 상태 (보류 중, 성공적인, 사용자 취소됨, 실패함)
  • 기간노출 시간, 재도장 시간 등의 세부 내역
  • 재료, 작업, 레이어인태스크커런트빌드 매개변수

3. 작업에 대한 사용자 메시지 가져오기¶

GET /api/v6.8/software/usermessages

사용자 메시지에 대한 세부 정보를 반환합니다. 여기에는 다음이 포함됩니다:

메시지 목록이 반환되거나, 메시지가 존재하지 않으면 아무것도 반환되지 않습니다.

  • 심각성사용자 메시지의 심각도
  • 작업ID빌드 작업의 고유 식별자,
  • 시간상승메시지가 생성된 타임스탬프,
  • 메시지: 메시지 자체,

이 API는 OAuth2 인증을 사용하며 데이터를 JSON 형식으로 반환합니다. 모든 타임스탬프는 UTC 기준입니다.

웹 API 인터페이스에 대한 전체 개요를 확인하려면 부록 API 문서를 참조하십시오.

환경 설정하기¶

먼저, 필요한 Python 패키지를 설치하고 임포트 설정을 해 봅시다.

[ ] 안에:
import 요청
import time
import json
from datetime import datetime, timezone
from 타입 import Dict, Optional, 튜플
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from dotenv import load_dotenv
import os
from datetime import timedelta

# 데모 목적으로 SSL 경고를 비활성화합니다
# 실제 운영 환경에서는 반드시 적절한 SSL 인증서를 사용하세요!
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

print("✓ 모든 패키지가 성공적으로 로드되었습니다!")

구성¶

연결 매개변수를 설정해 보겠습니다. 실제 운영 환경에서는 환경 변수나 보안 설정 파일을 통해 인증 정보를 사용하게 됩니다.

기계 인터페이스에 접근하려면 API 자격 증명을 생성해야 합니다. 이는 EOSCONNECT Core 통해 편리하게 수행할 수 있습니다. 브라우저에서 프린터의 웹 페이지를 엽니다. 본 사례에서는 https://si16120019/.

image.png

대부분의 경우 브라우저가 해당 사이트가 안전하지 않다는 경고를 표시합니다. 걱정하지 마세요. 이 경고는 EOS 머신이 기본적으로 자체 서명 인증서를 사용하기 때문에 나타납니다(부록의 '자체 서명 인증서 이해하기'도 참조하세요).

image-2.png

인증 설정 메뉴에서 (+) 버튼을 사용하여 새 클라이언트 ID와 시크릿 쌍을 생성할 수 있습니다.

image-3.png

최근 생성된 API 자격 증명은 .env 파일.

왜 ~을(를) 사용해야 할까요? .env 파일?

API 키나 비밀번호 같은 민감한 자격 증명을 코드에 직접 저장하는 것은 보안 위험입니다. 특히 노트북을 공유하거나 Git 같은 버전 관리 시스템에 커밋할 경우 더욱 그렇습니다. .env 파일은 깔끔한 해결책을 제공합니다:

  • 보안: 자격 증명은 코드와 분리되어 보관됩니다
  • 편의성: 코드를 수정하지 않고도 자격 증명을 쉽게 업데이트할 수 있습니다
  • 유연성: 서로 다른 환경(개발, 운영)은 서로 다른 .env 파일들
  • 모범 사례: 구성 관리를 위해 "12-팩터 앱" 방법론을 따릅니다

변수 설정하기¶

다음으로, 사용하고자 하는 EOSCONNECT Core API 버전을 결정합니다. EOSCONNECT Core 웹 API에 대한 하위 호환성을 EOSCONNECT Core , 최신 소프트웨어와 구형 소프트웨어를 모두 탑재한 기기를 웹 API를 통해 쿼리할 수 있습니다. 최신 버전은 기기에서 직접 읽어들입니다. 본 게시물 작성 시점 기준으로 사용 가능한 최신 버전은 버전 v6.8

[ ] 안에:
# Load environment variables from .env file
load_dotenv()

# Load API credentials from environment variables
HOSTNAME = os.getenv("HOSTNAME")
API_VERSION_INFO_URL = f"https://{HOSTNAME}/api/supportedVersions"

# Fetch available API versions

# Check if API_VERSION is set in environment variables
API_VERSION = os.getenv("API_VERSION")

if API_VERSION:
    # Use the version from environment variable
    print(f"✓ Using API version from environment variable: {API_VERSION}")

else:
    # Fetch API versions from the endpoint
    print(f"\n🔍 Fetching available API versions from {API_VERSION_INFO_URL}...")
    response = requests.get(API_VERSION_INFO_URL, verify=False, timeout=10)
    versions_data = response.json()
    # Extract version strings from the response
    available_versions = [f"v{v['majorVersion']}.{v['minorVersion']}" for v in versions_data]

    if available_versions:
        # Sort versions to get the latest
        API_VERSION = sorted(available_versions, key=lambda v: [int(x) for x in v.lstrip('v').split('.')])[-1]
        print(f"✓ Available API versions: {', '.join(available_versions)}")
        print(f"✓ Using latest version: {API_VERSION}")
    else:
        # Fallback to default version
        API_VERSION = "v6.0"
        print(f"⚠️  No versions found in response, using default: {API_VERSION}")

API자격 증명 로드하기¶

이제 우리는 API 자격 증명을 .env 메모리에 파일 저장

[ ] 안에:
from dotenv import load_dotenv
import os
from datetime import timedelta

# Load environment variables from .env file
load_dotenv()

API_BASE_URL = f"https://{HOSTNAME}/api/{API_VERSION}"
API_CLIENT_ID = os.getenv("API_CLIENT_ID")
API_CLIENT_SECRET = os.getenv("API_CLIENT_SECRET")

print("✓ Environment variables loaded")
print(f"  - Client ID: {API_CLIENT_ID}")
print(f"  - Client Secret: {'*' * len(API_CLIENT_SECRET) if API_CLIENT_SECRET else 'Not set'}")
print(f"  - Base URL: {API_BASE_URL}")

액세스토큰 가져오기¶

EOSCONNECT 웹 API와 통신하려면 액세스 토큰이 필요합니다. 이 토큰은 다음과 같은 기능을 하는 디지털 키 역할을 합니다:

  • 인증: API에 접근할 권한이 있음을 확인합니다.
  • 권한 부여: 수행이 허용되는 작업들을 정의합니다(클라이언트 권한에 기반).
  • 보안: 무단 접근으로부터 기기를 보호합니다

토큰은 OAuth2 클라이언트 자격 증명 흐름을 통해 요청됩니다. 이는 기계 간 통신을 위한 표준화된 절차입니다. EOSCONNECT Core 이전에 생성한 API 자격 증명(클라이언트 ID 및 시크릿)이 시간 제한 액세스 토큰으로 교환됩니다.

중요: 토큰은 유효 기간이 제한되어 있습니다(일반적으로 1시간). 장시간 실행되는 애플리케이션의 경우, 토큰이 만료되기 전에 반드시 갱신해야 합니다.

[ ] 안에:
def get_oauth_token(client_id: str, client_secret: str) -> Optional[Dict]:
    """
    Fetch OAuth2 token using client credentials flow.
    
    Args:
        client_id: OAuth2 client ID
        client_secret: OAuth2 client secret
        
    Returns:
        Dictionary containing token information or None if request fails
    """
    token_url = f"https://{HOSTNAME}/auth/connect/token"
    
    # Prepare the request data for client credentials grant
    data = {
        "grant_type": "client_credentials",
        "client_id": client_id,
        "client_secret": client_secret
    }
    
    headers = {
        "Content-Type": "application/x-www-form-urlencoded"
    }
    
    try:
        response = requests.post(token_url, data=data, headers=headers, verify=False, timeout=10)
        response.raise_for_status()
        token_data = response.json()
        return token_data
    except requests.exceptions.RequestException as e:
        print(f"❌ Error fetching OAuth token: {e}")
        return None


def refresh_token() -> bool:
    """
    Refresh the OAuth2 token and update global HEADERS.
    
    Returns:
        True if token refresh was successful, False otherwise
    """
    global API_TOKEN, HEADERS
    
    token_data = get_oauth_token(API_CLIENT_ID, API_CLIENT_SECRET)
    
    if token_data:
        API_TOKEN = token_data.get("access_token", "")
        token_type = token_data.get("token_type", "Bearer")
        
        HEADERS = {
            "Authorization": f"{token_type} {API_TOKEN}",
            "Accept": "application/json"
        }

        return True
    else:
        print("❌ Failed to refresh token")
        return False

# Fetch the token
token_response = get_oauth_token(API_CLIENT_ID, API_CLIENT_SECRET)

if token_response:
    # Extract token details
    API_TOKEN = token_response.get("access_token", "")
    token_type = token_response.get("token_type", "Bearer")
    expires_in = token_response.get("expires_in", 0)
    
    # Update the headers with the new token
    HEADERS = {
        "Authorization": f"{token_type} {API_TOKEN}",
        "Accept": "application/json"
    }
    
    # Calculate expiry time
    current_time = datetime.now(timezone.utc)
    expiry_time = current_time + timedelta(seconds=expires_in)
    
    # Display token information
    print("📋 Token Details:")
    print(f"  Token Type: {token_type}")
    print(f"  Expires In: {expires_in} seconds ({expires_in / 3600:.2f} hours)")
    print(f"  Current Time (UTC): {current_time.strftime('%Y-%m-%d %H:%M:%S')}")
    print(f"  Expiry Time (UTC): {expiry_time.strftime('%Y-%m-%d %H:%M:%S')}")
    print(f"  Token (first 50 chars): {API_TOKEN[:50]}...")
    print(f"\n✓ Authorization headers updated")
else:
    print("⚠️  Failed to retrieve token. Please check your credentials.")

1단계: API에 연결하기¶

API에서 작업 세부 정보와 사용자 메시지를 가져오는 함수를 만들어 보겠습니다. 이는 EOSCONNECT 인터페이스와 상호작용하는 것이 얼마나 간단한지 보여줍니다!

[ ] 안에:
def get_last_job() -> Optional[Dict]:
    """
    Fetch the most recent job from the printer.
    
    Returns:
        Dictionary containing the last job's details or None if request fails
    """
    url = f"{API_BASE_URL}/jobs/last"
    try:
        response = requests.get(url, headers=HEADERS, verify=False, timeout=10)
        response.raise_for_status()
        return response.json()
    except requests.exceptions.RequestException as e:
        print(f"❌ Error fetching last job: {e}")
        return None
    
def get_user_message(jobId : str, limit: int = 20) -> Optional[Dict]:
    """
    Fetch the current user message displayed on the printer.
    
    Returns:
        Dictionary containing the user message or None if request fails
    """
    url = f"{API_BASE_URL}/software/usermessages"
    params = {"jobId": jobId, "limit":limit}
    try:
        response = requests.get(url, headers=HEADERS, params=params, verify=False, timeout=10)
        response.raise_for_status()
        return response.json()
    except requests.exceptions.RequestException as e:
        print(f"❌ Error fetching user message: {e}")
        return None

def get_all_jobs(limit: int = 10, from_date: Optional[str] = None, to_date: Optional[str] = None) -> Optional[list]:
    """
    Fetch a list of recent jobs.
    
    Args:
        limit: Maximum number of jobs to retrieve
        from_date: Filter jobs starting from this date (ISO 8601 format, e.g., '2024-01-01T00:00:00Z')
        to_date: Filter jobs up to this date (ISO 8601 format, e.g., '2024-12-31T23:59:59Z')
        
    Returns:
        List of job summaries or None if request fails
    """
    url = f"{API_BASE_URL}/jobs"
    params = {"take": limit}
    
    # Add optional date filters if provided
    if from_date:
        params["from"] = from_date
    if to_date:
        params["to"] = to_date
    
    try:
        response = requests.get(url, headers=HEADERS, params=params, verify=False, timeout=10)
        response.raise_for_status()
        return response.json()
    except requests.exceptions.RequestException as e:
        print(f"❌ Error fetching jobs list: {e}")
        return None


print("✓ API functions defined")

2단계: 연결테스트¶

프린터에서 최근 작업들을 가져와서 연결 상태를 테스트해 봅시다. 이렇게 하면 데이터 구조를 이해하는 데 도움이 될 것입니다.

그 작업 가져오기 함수는 지정된 시간 범위 내에서 시작된 모든 작업을 시스템에서 조회합니다. 시작 날짜 그리고 현재까지. 우리는 이전에 정의된 API 함수를 사용합니다. 모든 작업 가져오기 이를 위해. API 함수의 결과는 형식화되어 Gradio UI에 표시됩니다. 그래픽 표현을 위한 코드는 EOSCONNECT Core Core와 무관하며 ui.py 명확성을 위해 파일을 제출합니다.

일자리에 대한 세부 사항을 살펴보겠습니다:

  • 그 id 작업에 대한 고유 식별자입니다. 안타깝게도 이 식별자는 기계 일련번호와 타임스탬프의 조합을 나타낼 뿐이므로, 우리의 작업(레시피)에 대한 정보를 전혀 제공하지 않습니다.
  • 그 결과 우리가 관심 있는 속성입니다. 작업이 완료되었는지 여부와 성공 여부를 알려주길 원합니다.
[ ] 안에:
import pandas as pd
from datetime import datetime, timedelta
from ui import create_job_browser_ui

def fetch_jobs(from_date, to_date):
    """
    Fetch jobs within the specified date range and display them in a table.
    
    Args:
        from_date: Start date for filtering jobs
        to_date: End date for filtering jobs
        
    Returns:
        Pandas DataFrame with job information
    """
    # Convert dates to ISO 8601 format with time
    from_datetime = f"{from_date}T00:00:00.000Z" if from_date else None
    to_datetime = f"{to_date}T23:59:59.999Z" if to_date else None
    
    # Fetch jobs from API
    refresh_token()
    jobs = get_all_jobs(limit=500, from_date=from_datetime, to_date=to_datetime)
    
    if not jobs:
        return pd.DataFrame(columns=['id', 'result', 'timeStarted', 'timeEnded', 'task', 'material'])
    
    # Extract relevant fields
    job_data = []
    for job in jobs:
        job_data.append({
            'id': job.get('id', 'N/A'),
            'result': job.get('result', 'N/A'),
            'timeStarted': job.get('timeStarted', 'N/A'),
            'timeEnded': job.get('timeEnded', 'N/A'),
            'task': job.get('task', 'N/A'),
            'material': job.get('material', 'N/A')
        })
    
    # Create DataFrame
    df = pd.DataFrame(job_data)
    return df

# Launch the interface
demo = create_job_browser_ui(fetch_jobs)
demo.launch(share=False, inline=True)

예시: 이것은 샘플 출력입니다 image.png

최근에 생성된 작업들의 개요를 확인할 수 있습니다. 이제 작업 결과에 따라 추가 세부 정보를 조회할지 여부를 결정할 수 있습니다. 본 사례에서는 작업이 취소되었거나 오류로 종료된 경우에만 추가 세부 정보(특히 사용자 메시지)에 관심이 있습니다.

3단계: 작업 모니터링로직¶

이제 핵심 모니터링 로직을 구현해 보겠습니다. 이 함수는 가장 최근에 빌드된 작업이 성공적으로 완료되었는지 여부를 확인합니다. 작업이 중단되거나 오류로 인해 종료된 경우, 취소와 관련된 정보를 수집하기 위해 사용자 메시지를 가져옵니다.

[ ] 안에:
def check_last_job_status(last_job_id: Optional[str] = None) -> Optional[Dict]:
    """
    Check the status of the last job.
    
    Args:
        last_job_id: Optional job ID to compare against. If provided and matches
                     the current last job, returns None (no change detected)
    
    Returns:
        Dictionary containing:
        - 'result': Job result status (Pending, Successful, Failed, UserCanceled)
        - 'jobId': The job ID
        - 'task': The task name
        - 'timeStarted': When the job started
        - 'timeEnded': When the job ended (None if still running)
        - 'userMessages': List of user messages (only for failed jobs)
        
        Returns None if last_job_id matches the current last job ID
    """
    refresh_token()
    # Fetch the last job
    last_job = get_last_job()
    
    if not last_job:
        return {
            'result': 'Error',
            'jobId': None,
            'task': None,
            'timeStarted': None,
            'timeEnded': None,
            'userMessages': None,
            'error': 'Failed to fetch last job'
        }
    
    job_id = last_job.get('id', 'Unknown')
    
    # If last_job_id is provided and matches current job, return None
    if last_job_id is not None and job_id == last_job_id:
        return None
    
    result = last_job.get('result', 'Unknown')
    task = last_job.get('task', 'N/A')
    time_started = last_job.get('timeStarted', 'N/A')
    time_ended = last_job.get('timeEnded', 'N/A')
    
    # Prepare base response
    response = {
        'result': result,
        'jobId': job_id,
        'task': task,
        'timeStarted': time_started,
        'timeEnded': time_ended,
        'userMessages': None
    }
    
    # If job failed or was canceled, fetch user messages
    if result in ['Failed', 'UserCanceled']:
        user_messages = get_user_message(job_id, limit=20)
        response['userMessages'] = user_messages if user_messages else []
    
    return response


print("✓ Last job status check function defined")
[ ] 안에:
import pandas as pd
from ui import create_job_status_ui

# Track the last job ID
last_job_id_tracker = None

def fetch_job_status():
    """
    Fetch the last job status and format it for display.
    Returns a tuple of (job_info_df, user_messages_df)
    """
    global last_job_id_tracker
    
    result = check_last_job_status(last_job_id_tracker)
    
    if result is None:
        # No new job detected
        return (
            pd.DataFrame([{"Info": f"No new job detected or job status unchanged. Tracked ID: {last_job_id_tracker}"}]),
            pd.DataFrame()
        )
    
    # Update tracker
    last_job_id_tracker = result.get('jobId')
    
    # Create job info DataFrame
    job_info = {
        'Field': ['Job ID', 'Task', 'Result', 'Time Started', 'Time Ended'],
        'Value': [
            result.get('jobId', 'N/A'),
            result.get('task', 'N/A'),
            result.get('result', 'N/A'),
            result.get('timeStarted', 'N/A'),
            result.get('timeEnded', 'N/A')
        ]
    }
    job_info_df = pd.DataFrame(job_info)
    
    # Create user messages DataFrame
    user_messages = result.get('userMessages')
    if user_messages:
        messages_data = []
        for msg in user_messages:
            messages_data.append({
                'Severity': msg.get('severity', 'N/A'),
                'timeRaised': msg.get('timeRaised', 'N/A'),
                'message': msg.get('message', 'N/A'),
                'additionalInfo': msg.get('additionalInfo', 'N/A')
            })
        user_messages_df = pd.DataFrame(messages_data)
    else:
        user_messages_df = pd.DataFrame([{"Info": "No user messages available"}])
    
    return job_info_df, user_messages_df

def clear_last_job_id_tracker():
    global last_job_id_tracker 
    last_job_id_tracker = None
    return [],[]
    

# Create Gradio interface for job status
job_status_demo = create_job_status_ui(fetch_job_status, clear_last_job_id_tracker)

# Launch the interface
job_status_demo.launch(share=False, inline=True)

예시: 이것은 샘플 출력입니다 image.png

이 예시에서 가장 최근에 빌드된 작업이 성공적으로 완료되지 않았음을 확인할 수 있습니다. 사용자 메시지를 살펴보면 기계 운영자가 작업을 중단했음을 알 수 있습니다.

부록:¶

OpenAPIEditor로 EOSCONNECT Core 문서 보기¶

다음 공식 Swagger 편집기를 사용할 수 있습니다: https://editor.swagger.io/ 렌더링하기 위해 스웨거.json 파일. 이 파일은 EOSCONNECT Core에서 제공하는 모든 사용 가능한 API 호출에 대한 개요를 제공합니다. 유사한 인터페이스는 EOSCONNECT Core 직접 제공되며, 웹 API / 데이터 포인트 섹션에서 확인할 수 있습니다.

  1. 공식 Swagger Editor로 이동하세요: https://editor.swagger.io/
  2. 파일 → URL 가져오기를 클릭하세요
  3. 선택하십시오 스웨거.json 파일

자체 서명인증서 이해하기¶

웹사이트에 연결할 때, 브라우저는 해당 사이트의 보안 인증서가 신뢰할 수 있는 기관(예: 잘 알려진 조직)에 의해 서명되었는지 확인합니다.

자체 서명 인증서는 신뢰할 수 있는 기관이 아닌 웹사이트 소유자가 직접 생성합니다. 이는 정부에서 발급받는 대신 스스로 신분증을 작성하는 것과 같습니다.

브라우저가 인증서의 신뢰성을 확인할 수 없어 경고 메시지를 표시합니다. 해당 인증서는 합법적일 수도(예: EOS 프린터의 경우) 위험할 수도 있습니다. EOS 기기의 경우, 인터넷상의 임의 웹사이트가 아닌 본인의 로컬 프린터에 연결하는 것이므로 안전합니다.

이렇게 생각해보세요: 프린터가 제3자의 보증 없이 "저를 믿어주세요, 제가 말하는 바로 그 프린터입니다"라고 말하는 것과 같습니다.