ExMachine.ServerMachine (ex_machine v0.1.3)

View Source

GenServer-based statechart execution server.

This module provides a GenServer wrapper around ExMachine statecharts, allowing statecharts to run as supervised processes that can handle events asynchronously and maintain their state across multiple interactions.

Features

  • Supervised Process: Runs as a GenServer that can be supervised
  • Asynchronous Event Processing: Handle events without blocking callers
  • State Persistence: Maintains statechart state across events
  • Event Queuing: Built-in event queue for handling concurrent events
  • Monitoring: Built-in logging and monitoring capabilities
  • Fault Tolerance: Automatic restart and recovery through supervisor trees

Usage

To use ExMachine.ServerMachine, you typically create a module that defines your statechart and then start it as a GenServer:

defmodule MyStatechartServer do
  use ExMachine.ServerMachine

  # Define your statechart
  def init_statechart() do
    %{
      initial: "idle",
      states: %{
        "idle" => %{
          transitions: %{
            "start" => "working"
          }
        },
        "working" => %{
          transitions: %{
            "complete" => "idle",
            "error" => "error"
          }
        },
        "error" => %{
          transitions: %{
            "reset" => "idle"
          }
        }
      }
    }
  end

  # Optional: customize initial context
  def init_context() do
    %{
      start_time: System.system_time(:second),
      error_count: 0
    }
  end
end

# Start the server
{:ok, pid} = MyStatechartServer.start_link(name: :my_statechart)

# Send events
MyStatechartServer.send_event(pid, :start)
MyStatechartServer.send_event(pid, {:complete, %{result: "success"}})

# Get current state
current_state = MyStatechartServer.get_state(pid)

Supervision

ExMachine.ServerMachine processes can be easily supervised:

defmodule MyApp.Supervisor do
  use Supervisor

  def start_link(init_arg) do
    Supervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
  end

  def init(_init_arg) do
    children = [
      {MyStatechartServer, name: :my_statechart}
    ]

    Supervisor.init(children, strategy: :one_for_one)
  end
end

Advanced Features

Custom Event Handling

defmodule AdvancedStatechartServer do
  use ExMachine.ServerMachine

  # Override handle_event for custom processing
  def handle_event(event, state) do
    # Custom preprocessing
    event = preprocess_event(event)

    # Call default handler
    result = super(event, state)

    # Custom postprocessing
    postprocess_result(result)
  end
end

State Change Callbacks

defmodule CallbackStatechartServer do
  use ExMachine.ServerMachine

  # Called whenever state changes
  def on_state_change(old_config, new_config, context) do
    Logger.info("State changed from #{inspect(old_config)} to #{inspect(new_config)}")
  end

  # Called on macrostep completion
  def on_macrostep_complete(completed_macrostep) do
    Metrics.increment("statechart.macrosteps")
    Metrics.histogram("statechart.transition_count", length(completed_macrostep.transitions))
  end
end

Error Handling

The ServerMachine includes built-in error handling for common scenarios:

  • Invalid Events: Events that don't trigger any transitions are ignored
  • Action Failures: Failed actions are logged but don't crash the server
  • Guard Failures: Failed guards prevent transitions but don't crash
  • Malformed Events: Invalid event formats are logged and ignored

Performance Considerations

  • Events are processed sequentially to maintain statechart semantics
  • Large contexts should avoid deep copying by using references
  • Consider using cast for fire-and-forget events
  • Use call when you need confirmation of event processing

NOTE

This module is currently a placeholder and needs implementation. The documentation above represents the intended API and functionality.