Source code for openfactory.schemas.connectors.mtconnect

"""
MTConnect Connector Schemas

This module provides Pydantic models to define and validate configuration
schemas for MTConnect adapters and agents within OpenFactory.

Key Models:
-----------
- Adapter:
  Configuration for MTConnect adapters, supporting either direct IP-based
  connection or container image deployment. Validates that exactly one of
  `ip` or `image` is specified and applies deployment defaults.

- Agent:
  Configuration for MTConnect agents, which may be external (specified by `ip`)
  or embedded with an Adapter and device XML file. Validates the correct
  mutual exclusivity and presence of `ip`, `device_xml`, and `adapter` fields.

- MTConnectConnectorSchema:
  Wrapper schema with a discriminator `type='mtconnect'` that encapsulates the
  Agent configuration.

Validation Features:
--------------------
- Ensures mutual exclusivity of critical fields (`ip` vs. `image` for adapters;
  `ip` vs. `adapter` and `device_xml` for agents).
- Provides default deployment settings (`deploy.replicas = 1`) if unspecified.
- Forbids unknown fields to ensure strict schema conformance.

YAML Example:
-------------
.. code-block:: yaml

    type: mtconnect
    agent:
      port: 7878
      device_xml: /path/to/device.xml
      adapter:
        ip: 192.168.0.201
        port: 7879

.. seealso::

   The runtime class of the MTConnect schema is :class:`openfactory.connectors.mtconnect.mtc_connector.MTConnectConnector`.
"""

from typing import Dict, List, Optional, Literal
from pydantic import BaseModel, ConfigDict, Field, model_validator
from openfactory.schemas.common import Deploy


[docs] class Adapter(BaseModel): """ MTConnect Adapter Schema. """ ip: Optional[str] = Field( default=None, description="IP address of the adapter. Must be specified if `image` is not." ) image: Optional[str] = Field( default=None, description="Container image name for the adapter. Must be specified if `ip` is not." ) port: int = Field( ..., description="Port number the adapter listens on." ) environment: Optional[List[str]] = Field( default=None, description="List of environment variables to set for the adapter container." ) deploy: Optional[Deploy] = Field( default=None, description="Deployment configuration such as resource limits and placement." ) model_config = ConfigDict(extra="forbid")
[docs] @model_validator(mode='before') def validate_adapter(cls, values: Dict) -> Dict: """ Validates the adapter configuration. Args: values (Dict): Dictionary of values to validate. Returns: Dict: Validated values. Raises: ValueError: If 'ip' or 'image' is missing or incorrectly defined. Note: Either 'ip' or 'image' must be specified, but not both. """ ip = values.get('ip') image = values.get('image') # Either 'ip' or 'image' must be specified, but not both if (ip is None and image is None) or (ip and image): raise ValueError("Either 'ip' or 'image' must be specified in the adapter.") return values
[docs] @model_validator(mode='after') def set_deploy_defaults(values: "Adapter") -> "Adapter": """ Ensures deploy is set and deploy.replicas has default value 1 if missing. Args: values (Adapter): Adapter instance after initial validation. Returns: Adapter: Adapter instance with defaults set. """ if values.deploy is None: values.deploy = Deploy(replicas=1) elif values.deploy.replicas is None: values.deploy.replicas = 1 return values
[docs] class Agent(BaseModel): """ MTConnect Agent Schema. """ ip: Optional[str] = Field( default=None, description="IP address of an external MTConnect agent. If set, 'adapter' and 'device_xml' must be omitted." ) port: int = Field( ..., description="Port number the agent listens on." ) device_xml: Optional[str] = Field( default=None, description="Path to the device XML file, required if 'ip' is not set." ) adapter: Optional[Adapter] = Field( default=None, description="Embedded adapter configuration, required if 'ip' is not set." ) deploy: Optional[Deploy] = Field( default=None, description="Deployment configuration such as resource limits and placement." ) model_config = ConfigDict(extra="forbid")
[docs] @model_validator(mode='after') def set_deploy_defaults(values: "Agent") -> "Agent": """ Sets default deployment configuration values after model initialization. Args: values (Agent): The Agent instance after initial validation. Returns: Agent: The Agent instance with default values set for deploy if missing. Notes: - If 'deploy' is None, it will be set to a Deploy instance with replicas=1. - If 'deploy' exists but 'replicas' is None, replicas will be set to 1. """ if values.deploy is None: values.deploy = Deploy(replicas=1) elif values.deploy.replicas is None: values.deploy.replicas = 1 return values
[docs] @model_validator(mode='before') def validate_agent(cls, values: Dict) -> Dict: """ Validates the agent configuration. Args: values (Dict): Dictionary of values to validate. Returns: Dict: Validated values. Raises: ValueError: If 'device_xml' or 'adapter' is missing or incorrectly defined. Note: - If 'ip' is None, both 'device_xml' and 'adapter' must be defined. - If 'ip' is set, 'device_xml' and 'adapter' must NOT be defined. """ ip = values.get('ip') adapter = values.get('adapter') if ip is None: if values.get('device_xml') is None: raise ValueError("'device_xml' is missing") if adapter is None: raise ValueError("'adapter' definition is missing") else: if adapter: raise ValueError("'adapter' can not be defined for an external agent") if values.get('device_xml'): raise ValueError("'device_xml' can not be defined for an external agent") return values
[docs] class MTConnectConnectorSchema(BaseModel): """ MTConnect Connector schema that wraps the Agent configuration. The `type` field is a discriminator for Pydantic to select this schema. .. seealso:: The runtime class of the MTConnect schema is :class:`openfactory.connectors.mtconnect.mtc_connector.MTConnectConnector`. """ type: Literal['mtconnect'] = Field( ..., # no default, means required description="Discriminator field to identify MTConnect connector type." ) agent: Agent = Field( ..., description="Configuration of the MTConnect agent." )