Hi All, I’m writing a tool that monitors some hardware and want to try following the OTP philosophy as closely as possible. I hope it’s ok if I attach some Elixir code, though the question is much more general.
def start_link(opts) do
GenServer.start_link(__MODULE__, :ok, opts)
end
def nickname_of(sensor, typ) do
repl = GenServer.call(MachineGovernor.MercuryITC, {:query, "READ:DEV:#{sensor}:#{typ}:NICK?\n"})
repl |> String.split(":") |> List.last()
end
def enum_devices() do
repl = GenServer.call(MachineGovernor.MercuryITC, {:query, "READ:SYS:CAT:DEV?\n"})
repl = String.split(repl, ":") |> Enum.drop(4) |> Enum.chunk_every(3)
Enum.map(repl, fn([sensor,typ,check]) ->
{sensor,typ,nickname_of(sensor,typ)}
end)
end
def sensor(typ, sensor) do
msg = case typ do
"HTR" -> "READ:DEV:#{sensor}:HTR:SIG:POWR?\n"
"TEMP" -> "READ:DEV:#{sensor}:TEMP:SIG:TEMP?\n"
"LVL" -> "READ:DEV:#{sensor}:LVL:SIG:HEL:LEV?\n"
end
repl = GenServer.call(MachineGovernor.MercuryITC, {:query, msg})
repl = String.split(repl, ":") |> List.last()
{f, ""} = String.slice(repl,0..-2) |> Float.parse
{f, String.slice(repl,-1..-1)}
end
@impl true
def init(:ok) do
{:ok, uart} = Circuits.UART.start_link
Circuits.UART.open(uart, "ttyACM1", speed: 9600, active: false)
Circuits.UART.configure(uart, framing: {Circuits.UART.Framing.Line, separator: "\n"})
{:ok, uart}
end
@impl true
def handle_call({:query, msg}, _from, uart) do
Circuits.UART.write(uart, msg)
{:ok, repl} = Circuits.UART.read(uart, 600)
{:reply, repl, uart}
end
What I’d like to know is if I’ve put the boundary between the server and the client in the right place. The exposed API could be pretty similar, and indeed the only relevant part of state is the PID for the serial port process. Additionally, is it the OTP way of doing things to just let the server get killed if the serial port drops? This seems to me to be the “non-defensive programming” way of doing things.