Skip to main content

Overview

The server implementation (backend/server.py) contains the actual conversion logic and servant class that handles remote requests from ICE clients.

Servant Class

The servant class inherits from the generated base class and implements all interface methods:
class ConversorUnidadesImpl(Conversor.ConversorUnidades):
    # Implementation of all methods
Conversor.ConversorUnidades is the base class generated by slice2py from the .ice file. Your servant must inherit from this class.

Conversion Algorithms

All conversion methods follow a common pattern: convert to a base unit first, then convert from the base unit to the target unit.

Temperature Conversion

Base unit: Celsius
backend/server.py
def convertirTemperatura(self, valor, desde, hasta, current=None):
    unidades = {"celsius", "fahrenheit", "kelvin"}
    desde, hasta = desde.lower(), hasta.lower()
    self._validar(desde, hasta, unidades, "temperatura")
    
    # Convert to Celsius first
    if desde == "fahrenheit":
        en_celsius = (valor - 32) * 5 / 9
    elif desde == "kelvin":
        en_celsius = valor - 273.15
    else:
        en_celsius = valor  # Already in Celsius
    
    # Convert from Celsius to target
    if hasta == "fahrenheit":
        return en_celsius * 9 / 5 + 32
    elif hasta == "kelvin":
        return en_celsius + 273.15
    return en_celsius  # Target is Celsius
The current parameter is required by ICE servants. It provides context about the remote invocation (client identity, connection info, etc.).
Conversion formulas:
  • Fahrenheit to Celsius: (F - 32) × 5/9
  • Kelvin to Celsius: K - 273.15
  • Celsius to Fahrenheit: C × 9/5 + 32
  • Celsius to Kelvin: C + 273.15

Length Conversion

Base unit: Meters (m)
backend/server.py
def convertirLongitud(self, valor, desde, hasta, current=None):
    unidades = {"m", "km", "mi", "ft"}
    desde, hasta = desde.lower(), hasta.lower()
    self._validar(desde, hasta, unidades, "longitud")
    
    # Conversion factors to meters
    a_metros = {"m": 1.0, "km": 1000.0, "mi": 1609.344, "ft": 0.3048}
    
    # Convert: value -> meters -> target unit
    return valor * a_metros[desde] / a_metros[hasta]
Conversion factors to meters:
  • 1 meter = 1.0 m
  • 1 kilometer = 1000.0 m
  • 1 mile = 1609.344 m
  • 1 foot = 0.3048 m

Weight Conversion

Base unit: Kilograms (kg)
backend/server.py
def convertirPeso(self, valor, desde, hasta, current=None):
    unidades = {"kg", "lb", "g"}
    desde, hasta = desde.lower(), hasta.lower()
    self._validar(desde, hasta, unidades, "peso")
    
    # Conversion factors to kilograms
    a_kg = {"kg": 1.0, "lb": 0.453592, "g": 0.001}
    
    # Convert: value -> kg -> target unit
    return valor * a_kg[desde] / a_kg[hasta]
Conversion factors to kilograms:
  • 1 kilogram = 1.0 kg
  • 1 pound = 0.453592 kg
  • 1 gram = 0.001 kg

Velocity Conversion

Base unit: Meters per second (m/s)
backend/server.py
def convertirVelocidad(self, valor, desde, hasta, current=None):
    unidades = {"kmh", "mph", "ms"}
    desde, hasta = desde.lower(), hasta.lower()
    self._validar(desde, hasta, unidades, "velocidad")
    
    # Conversion factors to meters/second
    a_ms = {"ms": 1.0, "kmh": 1/3.6, "mph": 0.44704}
    
    # Convert: value -> m/s -> target unit
    return valor * a_ms[desde] / a_ms[hasta]
Conversion factors to m/s:
  • 1 m/s = 1.0 m/s
  • 1 km/h = 1/3.6 m/s (approximately 0.277778 m/s)
  • 1 mph = 0.44704 m/s

Available Units Query

backend/server.py
def unidadesDisponibles(self, categoria, current=None):
    catalogo = {
        "temperatura": "celsius, fahrenheit, kelvin",
        "longitud":    "m, km, mi, ft",
        "peso":        "kg, lb, g",
        "velocidad":   "kmh, mph, ms",
    }
    cat = categoria.lower()
    if cat not in catalogo:
        ex = Conversor.UnidadInvalidaException()
        ex.mensaje = f"Categoria '{categoria}' no existe. Disponibles: {', '.join(catalogo)}"
        raise ex
    return catalogo[cat]

Validation and Error Handling

The private _validar method ensures that units are valid before attempting conversion:
backend/server.py
def _validar(self, desde, hasta, validas, categoria):
    errores = []
    if desde not in validas:
        errores.append(f"Unidad origen '{desde}' no valida para {categoria}.")
    if hasta not in validas:
        errores.append(f"Unidad destino '{hasta}' no valida para {categoria}.")
    if errores:
        ex = Conversor.UnidadInvalidaException()
        ex.mensaje = " ".join(errores) + f" Validas: {', '.join(sorted(validas))}"
        raise ex
Always throw ICE exceptions (defined in the .ice file) instead of Python exceptions. ICE exceptions can be serialized and propagated to clients.

Server Initialization

The main() function sets up and starts the ICE server:
1

Initialize the ICE runtime

with Ice.initialize(sys.argv) as communicator:
This processes command-line arguments like --Ice.Trace.Network=2 for debugging.
2

Create an Object Adapter

adapter = communicator.createObjectAdapterWithEndpoints(
    "ConversorAdapter", "default -p 10000"
)
ConversorAdapter
string
Adapter name (for configuration purposes)
default -p 10000
string
Endpoint specification: TCP protocol on port 10000
The adapter receives incoming requests and dispatches them to the appropriate servant.
3

Create and register the servant

servant = ConversorUnidadesImpl()
adapter.add(servant, Ice.stringToIdentity("ConversorUnidades"))
The string "ConversorUnidades" is the object identity that clients will use to locate this servant.
4

Activate the adapter

adapter.activate()
print("Servidor escuchando en puerto 10000... (Ctrl+C para detener)")
After activation, the server starts accepting incoming connections.
5

Wait for shutdown

communicator.waitForShutdown()
This blocks the main thread until the server receives a shutdown signal (Ctrl+C).

Complete Server Code

import sys
import os
import Ice

CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
PROJECT_ROOT = os.path.dirname(CURRENT_DIR)
if PROJECT_ROOT not in sys.path:
    sys.path.insert(0, PROJECT_ROOT)

import Conversor

class ConversorUnidadesImpl(Conversor.ConversorUnidades):

    def convertirTemperatura(self, valor, desde, hasta, current=None):
        unidades = {"celsius", "fahrenheit", "kelvin"}
        desde, hasta = desde.lower(), hasta.lower()
        self._validar(desde, hasta, unidades, "temperatura")

        if desde == "fahrenheit":
            en_celsius = (valor - 32) * 5 / 9
        elif desde == "kelvin":
            en_celsius = valor - 273.15
        else:
            en_celsius = valor

        if hasta == "fahrenheit":
            return en_celsius * 9 / 5 + 32
        elif hasta == "kelvin":
            return en_celsius + 273.15
        return en_celsius

    def convertirLongitud(self, valor, desde, hasta, current=None):
        unidades = {"m", "km", "mi", "ft"}
        desde, hasta = desde.lower(), hasta.lower()
        self._validar(desde, hasta, unidades, "longitud")

        a_metros = {"m": 1.0, "km": 1000.0, "mi": 1609.344, "ft": 0.3048}
        return valor * a_metros[desde] / a_metros[hasta]

    def convertirPeso(self, valor, desde, hasta, current=None):
        unidades = {"kg", "lb", "g"}
        desde, hasta = desde.lower(), hasta.lower()
        self._validar(desde, hasta, unidades, "peso")

        a_kg = {"kg": 1.0, "lb": 0.453592, "g": 0.001}
        return valor * a_kg[desde] / a_kg[hasta]

    def convertirVelocidad(self, valor, desde, hasta, current=None):
        unidades = {"kmh", "mph", "ms"}
        desde, hasta = desde.lower(), hasta.lower()
        self._validar(desde, hasta, unidades, "velocidad")

        a_ms = {"ms": 1.0, "kmh": 1/3.6, "mph": 0.44704}
        return valor * a_ms[desde] / a_ms[hasta]

    def unidadesDisponibles(self, categoria, current=None):
        catalogo = {
            "temperatura": "celsius, fahrenheit, kelvin",
            "longitud":    "m, km, mi, ft",
            "peso":        "kg, lb, g",
            "velocidad":   "kmh, mph, ms",
        }
        cat = categoria.lower()
        if cat not in catalogo:
            ex = Conversor.UnidadInvalidaException()
            ex.mensaje = f"Categoria '{categoria}' no existe. Disponibles: {', '.join(catalogo)}"
            raise ex
        return catalogo[cat]

    def _validar(self, desde, hasta, validas, categoria):
        errores = []
        if desde not in validas:
            errores.append(f"Unidad origen '{desde}' no valida para {categoria}.")
        if hasta not in validas:
            errores.append(f"Unidad destino '{hasta}' no valida para {categoria}.")
        if errores:
            ex = Conversor.UnidadInvalidaException()
            ex.mensaje = " ".join(errores) + f" Validas: {', '.join(sorted(validas))}"
            raise ex

def main():
    with Ice.initialize(sys.argv) as communicator:
        adapter = communicator.createObjectAdapterWithEndpoints(
            "ConversorAdapter", "default -p 10000"
        )
        
        servant = ConversorUnidadesImpl()
        adapter.add(servant, Ice.stringToIdentity("ConversorUnidades"))
        adapter.activate()
        
        print("Servidor escuchando en puerto 10000... (Ctrl+C para detener)")
        communicator.waitForShutdown()

if __name__ == "__main__":
    main()

Running the Server

1

Ensure generated stubs exist

Run slice2py on the .ice file if you haven’t already:
slice2py backend/Conversor.ice
2

Start the server

python backend/server.py
You should see:
Servidor escuchando en puerto 10000... (Ctrl+C para detener)
3

The server is now ready

Clients can now connect to localhost:10000 and invoke remote methods.

Key Architecture Concepts

Servant

The server-side object that implements the interface methods defined in the .ice file.

Object Adapter

The component that receives incoming requests and routes them to the correct servant based on object identity.

Object Identity

A unique name (“ConversorUnidades”) that clients use to locate the servant within the adapter.

Endpoint

The network address and port where the server listens for connections (TCP port 10000).

Debugging Tips

Enable ICE tracing for debugging:
python backend/server.py --Ice.Trace.Network=2 --Ice.Trace.Protocol=1
This shows detailed information about network connections and protocol messages.

Next Steps

Client Usage

Learn how to connect to the server and make remote calls

Build docs developers (and LLMs) love