Измените состояние gen_fsm на функцию в другом модуле

У нас есть довольно большое USSD-приложение, которое использует модуль Erlang gen_fsm для управления пунктами меню.

Текущая версия имеет один файл menus_fsm.erl, содержащий более 5000 строк кода, связанного с gen_fsm. Наша следующая версия дает нам возможность разделить menus_fsm.erl на отдельные файлы, чтобы сделать его более удобным в обслуживании в будущем.

В старой версии для отображения меню справки мы делаем следующее (help_menu/1 вызывается из непоказанного кода, отображающего главное меню):

-module(menus_fsm).    
% Snipped some irrelvant code

help_menu(StateData) ->
    % Display the first menu
    send_menu(StateData, "Please Select:\n1. Option 1\n2. Option 2"),
    {next_state, waitHelpMenuChoice, StateData, ?MENU_TOUT};

waitHelpMenuChoice(Params, StateData) ->
    io:format("Got Help menu response: ~p", [Params]),
    doTerminate(ok,"Help Menu", StateData).

Я пропустил большую часть кода, показывающего точку входа в FSM и так далее.

В новой версии мы хотим переместить help_menu/1 и waitHelpMenuChoice/2 в новый модуль help_menu, который вызывается из menus_fsm, вот так:

-module( help_menu ).    
% Snipped some irrelevant code

help_menu(StateData) ->
    menus_fsm:send_menu(StateData, "Please Select:\n1. Option 1\n2. Option 2"),
    {next_state, waitHelpMenuChoice, StateData, ?MENU_TOUT};

waitHelpMenuChoice(Params, StateData) ->
    io:format("Got Help menu response: ~p", [Params]),
    menus_fsm:doTerminate(ok,"Help Menu", StateData).

Проблема в строке {next_state, waitHelpMenuChoice, StateData, ?MENU_TOUT};: gen_fsm ожидает, что waitHelpMenuChoice будет в модуле menus_fsm, что возвращает меня к тому, с чего мы начали.

Я попытался заменить проблемную строку на

{next_state, fun help_menu:waitHelpMenuChoice/2, StateData, ?MENU_TOUT};

но это просто приводит к ошибке, подобной следующей: {badarg,[{erlang,apply,[conv_fsm,#Fun<help_menu.waitHelpMenuChoice.2>,[]]}

Есть ли у кого-нибудь предложения, как это обойти?


person Wernsey    schedule 05.04.2012    source источник


Ответы (2)


Возможно, вы могли бы использовать http://www.erlang.org/doc/man/gen_fsm.html#enter_loop-6 для этого? Не уверен, что это сработает, чтобы вызвать его внутри другого fsm, но, возможно, стоит попробовать.

person Lukas    schedule 05.04.2012

Мне удалось найти решение собственного вопроса. Если это кажется очевидным, это может быть потому, что я немного новичок в Erlang.

Я добавил новую функцию wait_for_menu_response/2 в модуль menus_fsm, которая обрабатывает переходы между состояниями от имени других модулей.

-module(menus_fsm),
-export([wait_for_menu_response/2]).
% ...snip...
wait_for_menu_response(Params, {Function, StateData}) ->
    Function(Params, StateData).

Затем модуль help_menu был изменен следующим образом:

-module( help_menu ).    
% ...snip...

help_menu(StateData) ->
    menus_fsm:send_menu(StateData, "Please Select:\n1. Option 1\n2. Option 2"),
    {next_state, wait_for_menu_response, {fun waitHelpMenuChoice/2, StateData}, ?MENU_TOUT}.

waitHelpMenuChoice(Params, StateData) ->
    io:format("Got Help menu response: ~p", [Params]),
    menus_fsm:doTerminate(ok,"Help Menu", StateData).

поэтому gen_fsm остается в модуле menus_fsm, когда он вызывает wait_for_menu_response, но теперь wait_for_menu_response может вызывать help_menu:waitHelpMenuChoice/2. help_menu:waitHelpMenuChoice/2 не нуждался в каких-либо изменениях.

На самом деле, в моей окончательной версии функция menus_fsm:send_menu была изменена, чтобы принимать fun waitHelpMenuChoice/2 в качестве третьего параметра, так что функция help_menu просто стала такой:

help_menu(StateData) ->
    menus_fsm:send_menu(StateData, "Please Select:\n1. Option 1\n2. Option 2", 
        fun waitHelpMenuChoice/2).

но я думаю, что мое объяснение выше иллюстрирует идею лучше.

person Wernsey    schedule 05.04.2012