Hi,
Long time reader, first time poster here
I originally posted this question at stack overflow so I am reposting it here with the hope of receiving suggestions ( and after getting some ideas from this ExForum thread). This is a use-case that I’d find very handy to master. Re-post below:
I’m trying to model a simple oscillator that is continuously running in the background (integrating a sine function). However at some point I want to be able to request its value (voltage and time), which is kept in its internal state. That is because at a latter point I will want a pool of oscillators supervised, and their Supervisor will average the voltage/values, and other handful operations.
I reached this approach, which I’m not 100% happy with, since it’s a bit of a pain to have to run run()
before exiting the get_state
server implementation, ie. handle_call({:get_state, pid}.....)
.
Is there any other approach I could give a try to?
defmodule World.Cell do
use GenServer
@timedelay 2000
# API #
#######
def start_link do
GenServer.start_link(__MODULE__, [], [name: {:global, __MODULE__}])
end
def run do
GenServer.cast({:global, __MODULE__}, :run)
end
def get_state(pid) do
GenServer.call(pid, {:get_state, pid})
end
# Callbacks #
#############
def init([]) do
:random.seed(:os.timestamp)
time = :random.uniform
voltage = :math.sin(2 * :math.pi + time)
state = %{time: time, voltage: voltage }
{:ok, state, @timedelay}
end
def handle_cast(:run, state) do
new_time = state.time + :random.uniform/12
new_voltage = :math.sin(2 * :math.pi + new_time)
new_state = %{ time: new_time, voltage: new_voltage }
IO.puts "VALUES #{inspect self()} t/v #{new_time}/#{new_voltage}"
{:noreply, new_state, @timedelay}
end
def handle_info(:timeout, state) do
run() # <------------ ALWAYS HAS TO RUN IT
{:noreply, state, @timedelay}
end
def handle_call({:get_state, pid}, _from, state) do
IO.puts "getting state"
run() # <------- RUN UNLESS IT STOPS after response
{:reply, state, state}
end
end