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で表示されます。

Web APIインターフェースの完全な概要については、付録「APIドキュメント」を参照してください。

環境設定¶

まず、必要なPythonパッケージをインストールし、インポートを設定しましょう。

[ ]内:
import requests
インポート time
import json
from datetime import datetime, タイムゾーン
from typing 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.例外.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 。つまり、現行ソフトウェアと旧ソフトウェアの両方を搭載したマシンをWeb 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 Web 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: 接続のテスト¶

プリンターから最近のジョブを取得して接続をテストしましょう。これによりデータ構造を理解できます。

その fetch_jobs 関数は、指定された時間範囲内で開始されたすべてのジョブをマシンに問い合わせます。 開始日 そして to_date. 事前に定義されたAPI関数を使用します すべてのジョブを取得 この目的のため。API関数の結果はフォーマットされ、Gradio UIに表示される。グラフィカル表現のEOSCONNECT Core でありEOSCONNECT Core ui.py 明確化のためにファイルを作成する。

仕事の詳細を見てみましょう:

  • その id ジョブを一意に識別する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

この例では、最も最近ビルドされたジョブが正常に完了しなかったことがわかります。ユーザーメッセージを確認すると、機械オペレーターがジョブを中止したことがわかります。

付録:¶

OpenAPI EditorでEOSCONNECT Core ドキュメントを表示する¶

以下の公式Swagger Editorをご利用いただけます: https://editor.swagger.io/ レンダリングする swagger.json このファイルは、EOSCONNECT Coreが提供するすべての利用可能なAPI呼び出しの概要を示しています。同様のインターフェースEOSCONNECT Core 直接提供されており、Web API / データポイントセクションで確認できます。

  1. 公式のSwagger Editorにアクセスしてください:https://editor.swagger.io/
  2. ファイル→URLのインポートをクリック
  3. 選択してください swagger.json ファイル

自己署名証明書の理解¶

ウェブサイトに接続する際、ブラウザはサイトのセキュリティ証明書が信頼できる機関(有名な組織など)によって署名されているかどうかを確認します。

自己署名証明書は、信頼できる認証機関ではなく、ウェブサイト所有者自身が作成します。これは、政府から発行される身分証明書ではなく、自分で身分証明書を作成するようなものです。

お使いのブラウザは、証明書が信頼できるかどうかを確認できないため警告を表示しています。これは正規のもの(EOSプリンターなど)である可能性もあれば、潜在的に危険なものである可能性もあります。EOSマシンの場合、インターネット上のランダムなウェブサイトではなく、ご自身のローカルプリンターに接続しているため安全です。

つまり、プリンターが「私を信じてください、私は私が言う通りの存在です」と、第三者の保証なしに主張しているようなものだ。