I have a number of relatively simple (auto-generated) graphs in graphviz dot format. These show the path through a state machine, but dot has a slightly confusing habit of deciding that two nodes must be on the same rank when I would like the graph to be in state order. I've tried a lot of settings (including the :n and :s and the weight listed below), but I cannot persuade dot to place the Third State above the Fourth State.
I have this problem with a lot of graphs: there seems to be something internal to dot that decides that it would be better if two nodes were on the same rank and there's nothing that can be done to override it. I've even had code that specifies that one node should be a rank=sink, but dot has decided to put another node below it anyway.
Is there any way to suggest to dot that it's more important that the nodes are in order than any other constraint?
The code that was used to generate the graph looks like this:
digraph {
ERROR [label="Error"];
FirstSTATE [label="Initial State" URL="\ref FirstSTATE"];
FirstSTATE -> SecondSTATE;
SecondSTATE [label="Second State" URL="\ref SecondSTATE"];
SecondSTATE -> ThirdSTATE;
ThirdSTATE [label="Third State" URL="\ref ThirdSTATE"];
FourthSTATE [label="Fouth State?" shape="diamond"];
ThirdSTATE:s -> FourthSTATE:n [weight=50];
FourthSTATE -> FifthSTATE [label="Yes" ];
FourthSTATE -> ThirdSTATE [label="No"];
FifthSTATE [label="Fifth State" URL="\ref FifthSTATE"];
SixthSTATE [label="Sixth State?" shape="diamond"];
SixthSTATE -> ERROR [label="Yes" ];
SixthSTATE -> SeventhSTATE [label="No"];
FifthSTATE -> SixthSTATE;
SeventhSTATE [label="Seventh State" URL="\ref SeventhSTATE"];
SeventhSTATE -> EighthSTATE;
EighthSTATE [label="Eighth State" URL="\ref EighthSTATE"];
NinthSTATE [label="Ninth State?" shape="diamond"];
NinthSTATE -> TenthSTATE [label="Yes" ];
NinthSTATE -> EighthSTATE [label="No"];
EighthSTATE -> NinthSTATE;
TenthSTATE [label="Tenth State" URL="\ref TenthSTATE"];
EleventhSTATE [label="Eleventh State?" shape="diamond"];
EleventhSTATE -> ERROR [label="Yes" ];
EleventhSTATE -> TwelfthSTATE [label="No" ];
TenthSTATE -> EleventhSTATE;
TwelfthSTATE [label="Twelfth State" URL="\ref TwelfthSTATE"];
}
The graph currently looks like this:
Use "constraint=false".
http://www.graphviz.org/doc/info/attrs.html#d:constraint
In your graph:
FourthSTATE -> ThirdSTATE [label="No" constraint=false] ;
You'll get:
digraph {
ERROR [label="Error"];
FirstSTATE [label="Initial State" URL="\ref FirstSTATE"];
FirstSTATE -> SecondSTATE;
SecondSTATE [label="Second State" URL="\ref SecondSTATE"];
SecondSTATE -> ThirdSTATE;
ThirdSTATE [label="Third State" URL="\ref ThirdSTATE"];
FourthSTATE [label="Fouth State?" shape="diamond"];
ThirdSTATE -> FourthSTATE;
FourthSTATE -> FifthSTATE [label="Yes" ];
FourthSTATE -> ThirdSTATE [label="No" constraint=false] ;
FifthSTATE [label="Fifth State" URL="\ref FifthSTATE"];
SixthSTATE [label="Sixth State?" shape="diamond"];
SixthSTATE -> ERROR [label="Yes" ];
SixthSTATE -> SeventhSTATE [label="No"];
FifthSTATE -> SixthSTATE;
SeventhSTATE [label="Seventh State" URL="\ref SeventhSTATE"];
SeventhSTATE -> EighthSTATE;
EighthSTATE [label="Eighth State" URL="\ref EighthSTATE"];
NinthSTATE [label="Ninth State?" shape="diamond"];
NinthSTATE -> TenthSTATE [label="Yes" ];
NinthSTATE -> EighthSTATE [label="No"];
EighthSTATE -> NinthSTATE;
TenthSTATE [label="Tenth State" URL="\ref TenthSTATE"];
EleventhSTATE [label="Eleventh State?" shape="diamond"];
EleventhSTATE -> ERROR [label="Yes" ];
EleventhSTATE -> TwelfthSTATE [label="No" ];
TenthSTATE -> EleventhSTATE;
TwelfthSTATE [label="Twelfth State" URL="\ref TwelfthSTATE"];
}
Whenever you want an arrow that points upward, write the edge from top to bottom (i.e. backwards), then add dir=back (https://graphviz.org/docs/attrs/dir/) to make the arrow point upward instead of downward.
So instead of:
FourthSTATE -> ThirdSTATE [label="No"];
write:
ThirdSTATE -> FourthSTATE [dir=back, label="No"];
Now since all the edges go from ThirdSTATE to FourthSTATE, the rank is no longer ambiguous and Graphviz will reliably put ThirdSTATE above FourthSTATE.
This is often better than using constraint=false since edges with constraint=false are sometimes given very wiggly edges (as if they did not participate in graph layout?).
Related
I'm having trouble finding good documentation for the gcroot command as it applies to .NET core code so it's making it hard to follow some very weird gcroot output I have.
Below you'll see the top part of the output I'm talking about and the very weird route it takes. I didn't post the entire thing as it just keeps being very weird but is there some sort of logic one can use to figure out which of these actually apply? It almost looks like things that both reference the same thing are being included on the path.
rbp-8: 00007ffcdac29f18
-> 00007F47D41DDCA8 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Threading.Tasks.VoidTaskResult, System.Private.CoreLib],[Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions+<RunAsync>d__4, Microsoft.Extensions.Hosting.Abstractions]]
-> 00007F48D40E9560 Microsoft.Extensions.Hosting.Internal.Host
-> 00007F48D40E8380 Microsoft.Extensions.Logging.Logger`1[[Microsoft.Extensions.Hosting.Internal.Host, Microsoft.Extensions.Hosting]]
-> 00007F48D40E8478 Microsoft.Extensions.Logging.Logger
-> 00007F48D40E84A0 Microsoft.Extensions.Logging.LoggerInformation[]
-> 00007F48D40E8718 NLog.Extensions.Logging.NLogLogger
-> 00007F48D40E8500 NLog.Logger
-> 00007F48D40E86E0 NLog.Internal.LoggerConfiguration
-> 00007F48D40E8548 NLog.Internal.TargetWithFilterChain[]
-> 00007F48D40E8590 NLog.Internal.TargetWithFilterChain
-> 00007F47D4102560 NLog.Targets.Wrappers.AsyncTargetWrapper
-> 00007F47D4159DD0 System.Collections.Generic.List`1[[NLog.Layouts.Layout, NLog]]
-> 00007F47D4159DF0 NLog.Layouts.Layout[]
-> 00007F47D4102268 NLog.Layouts.SimpleLayout
-> 00007F47D40F1E48 NLog.Config.XmlLoggingConfiguration
-> 00007F47D40F1F28 System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib],[NLog.Targets.Target, NLog]]
-> 00007F47D411DC68 System.Collections.Generic.Dictionary`2+Entry[[System.String, System.Private.CoreLib],[NLog.Targets.Target, NLog]][]
-> 00007F47D410D798 NLog.Targets.Wrappers.AsyncTargetWrapper
-> 00007F47D4103BE0 NLog.Targets.FileTarget
-> 00007F47D412C038 NLog.Internal.FileAppenders.FileAppenderCache
-> 00007F47D412C180 System.Threading.Timer
-> 00007F47D412C1F8 System.Threading.TimerHolder
-> 00007F47D412C198 System.Threading.TimerQueueTimer
-> 00007F47D4049098 System.Threading.TimerQueue
-> 00007F4824900220 System.Threading.TimerQueueTimer
-> 00007F47D4048FC0 System.Threading.TimerQueueTimer
-> 00007F482732D6B8 System.Threading.TimerQueueTimer
-> 00007F492797C860 System.Threading.TimerQueueTimer
-> 00007F4927978F28 System.Threading.TimerQueueTimer
-> 00007F47D48F1BD8 System.Threading.TimerQueueTimer
-> 00007F4924F62E10 System.Threading.TimerQueueTimer
-> 00007F492735E210 System.Threading.TimerQueueTimer
-> 00007F47D40DF710 System.Threading.TimerQueueTimer
-> 00007F4926FE91F8 System.Threading.TimerQueueTimer
-> 00007F4926FE71D8 System.Threading.TimerQueueTimer
-> 00007F482662DA08 System.Threading.TimerQueueTimer
-> 00007F49215F3B70 System.Threading.TimerQueueTimer
-> 00007F48D41C20E0 System.Threading.TimerQueueTimer
-> 00007F48252EEAF0 System.Threading.TimerQueueTimer
-> 00007F48D80F1E48 System.Threading.TimerQueueTimer
-> 00007F481925FC30 System.Threading.TimerQueueTimer
-> 00007F481925FBD8 System.Threading.TimerCallback
-> 00007F481925F080 Microsoft.Data.ProviderBase.DbConnectionPool
-> 00007F47D48F1A70 Microsoft.Data.SqlClient.SqlConnectionFactory
-> 00007F492795E7A8 System.Collections.Generic.Dictionary`2[[Microsoft.Data.Common.DbConnectionPoolKey, Microsoft.Data.SqlClient],[Microsoft.Data.ProviderBase.DbConnectionPoolGroup, Microsoft.Data.SqlClient]]
-> 00007F492795E820 System.Collections.Generic.Dictionary`2+Entry[[Microsoft.Data.Common.DbConnectionPoolKey, Microsoft.Data.SqlClient],[Microsoft.Data.ProviderBase.DbConnectionPoolGroup, Microsoft.Data.SqlClient]][]
-> 00007F47D48F3D38 Microsoft.Data.ProviderBase.DbConnectionPoolGroup
<snip>
I'm fairly new to Erlang, and I'm trying to set up a simple multi-client chat server to learn more about the language. However whenever I try and run it I get a socket closed error from the call of gen_tcp:accept on the listening socket. I've tried a number of different port numbers to no avail. What am I missing?
The code is below:
-define(TCP_OPTIONS, [binary, {packet, 2}, {active, false}, {reuseaddr, true}]).
listen(Portno, DictPid) ->
case gen_tcp:listen(Portno, ?TCP_OPTIONS) of
{ok, ListenSocket} ->
spawn_link(fun() -> accept_connections(ListenSocket, DictPid) end),
io:format("Listening on ~p~n", [ListenSocket]);
{error, Error} ->
io:format("Listen Error: ~w~n", [Error])
end.
accept_connections(ListenSocket, DictPid) ->
case gen_tcp:accept(ListenSocket) of
{ok, ClientSocket} ->
io:format("Accepting:~w~n", [ClientSocket]),
gen_tcp:send(ClientSocket, "Welcome! Enter your name~n"),
ClientPid = spawn(fun() -> io:format("Client connected"),
setup_user(ClientSocket, DictPid) end),
gen_tcp:controlling_process(ClientSocket, ClientPid),
accept_connections(ListenSocket, DictPid);
{error, Error} ->
io:format("Accept Error: ~w~n", [Error])
end.
setup_user(ClientSocket, DictPid) ->
{ok, Username} = gen_tcp:recv(ClientSocket, 0),
DictPid ! {add_new_pair, ClientSocket, Username},
ClientDict = get_dict(DictPid),
broadcast_message(dict:fetch_keys(ClientDict), "[" ++ Username ++ "has entered the chat]"),
client_loop(ClientSocket, Username, DictPid).
[rest of program]
The issue here is that the controller of the ListenSocket terminates, which causes the ListenSocket to be closed.
I'm trying to make a recursive call to handle_call/3 before replying.
But it seems to not be possible, because a timeout exit exception is thrown.
Below you can see the code and the error.
The code:
-module(test).
-behavior(gen_server).
%% API
-export([start_link/0,init/1,handle_info/2,first_call/1,second_call/1, handle_call/3, terminate/2]).
-record(state, {whatever}).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
init(_Args) ->
{ok, #state{}}.
handle_info(Data, State) ->
{noreply, State}.
% synchronous messages
handle_call(_Request, _From, State) ->
case _Request of
first_call ->
{reply, data1, State};
second_call ->
{reply, {data2, first_call(self())}, State}
end.
first_call(Pid) ->
gen_server:call(Pid, first_call).
second_call(Pid) ->
gen_server:call(Pid, second_call).
terminate(_Reason, _State) ->
ok.
The error:
2> {_, PID} = test:start_link().
{ok,<0.64.0>}
3> test:second_call(PID).
** exception exit: {timeout,{gen_server,call,[<0.64.0>,second_call]}}
in function gen_server:call/2 (gen_server.erl, line 204)
A possible reason for this behaviour is that gen_server is not able to handle recursive calls until the first one is done (creating a deadlock). Is this the right reason, and if yes why?
Thanks.
Yes, that is the reason. The gen_server is a single process, and while handle_call is executing it doesn't pick up any messages nor respond to any gen_server:call. Therefore first_call(self()) times out.
I'm currently trying to create a simple chat server with socket.io-erlang. I just started learning Erlang so I have a few problems adapting their demo so it works with modules. Hope you can help me, here's what I have so far. It shouldn't have any features yet, this time I only want to make it work (I get a few crash reports after starting it, you can read them if you want).
App
-module(echat_app).
-behaviour(application).
-export([start/2, stop/1]).
start(_StartType, _StartArgs) ->
echat_sup:start_link().
stop(_State) ->
ok.
Supervisor
-module(echat_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1]).
-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
application:start(sasl),
application:start(gproc),
application:start(misultin),
application:start(socketio),
{ok, Pid} = socketio_listener:start([{http_port, 7878}, {default_http_handler,echat_http_handler}]),
EventManager = socketio_listener:event_manager(Pid),
ok = gen_event:add_handler(EventManager, echat_socketio_handler,[]),
receive _ ->
io:format("sub received something"),
{ok, {
{one_for_one, 5, 10},
[]
}}
end.
Socket.IO Event Handler
-module(echat_socketio_handler).
-behaviour(gen_event).
-include_lib("socketio/include/socketio.hrl").
-export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2, code_change/3]).
init([]) ->
{ok, undefined}.
handle_event({client, Pid}, State) ->
io:format("Connected: ~p~n",[Pid]),
EventManager = socketio_client:event_manager(Pid),
ok = gen_event:add_handler(EventManager, ?MODULE,[]),
{ok, State};
handle_event({disconnect, Pid}, State) ->
io:format("Disconnected: ~p~n",[Pid]),
{ok, State};
handle_event({message, Client, Msg=#msg{content=Content}}, State) ->
io:format("Got a message: ~p from ~p~n",[Msg, Client]),
socketio_client:send(Client, #msg{ content = "hello!" }),
socketio_client:send(Client, #msg{ content = [{<<"echo">>, Content}], json = true}),
{ok, State};
handle_event(_E, State) ->
{ok, State}.
handle_call(_, State) ->
{reply, ok, State}.
handle_info(_, State) ->
{ok, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
HTTP Request Handler
-module(echat_http_handler).
-export([handle_request/3]).
handle_request(_Method, _Path, Req) ->
Req:respond(200).
just a few things, no a specific answer to your question.
First, in general you start the dependencies before starting your app, rather than on the init of some supervisor.
Regarding the errors:
{error,{{noproc,{gen_server,call,
[socketio_listener_sup_sup,
{start_child,[[{http_port,7878},
{default_http_handler,echat_http_handler}]]},
infinity]}},
{echat_app,start,[normal,[]]}}}
This is the first one, it means that the code tried to call a gen_server named 'socketio_listener_sup_sup' , but that process did not exist. Given the name of it, I guess that is something that should be started by the socket.io-erlang application itself, so maybe it is not starting correctly for some reason. You can check that easily by checking the results:
ok = application:start(sasl),
ok = application:start(gproc),
ok = application:start(misultin),
ok = application:start(socketio),
I am starting to learn Erlang in the hopes of creating a game server to real-time multiplayer games. Currently, I am trying to estimate the amount of work and headache Erlang would cause vs. Scala. So, to start, I am creating a simple Erlang server process. I found a nice tutorial by Jesse Farmer which I have modified to learn more. My modified code is meant to be similar to his echo server, except it takes in English words and simply returns the Lojban equivalent. However, only the wildcard case is ever selected. Here is the code:
-module(translate).
-export([listen/1]).
-import(string).
-define(TCP_OPTIONS, [binary, {packet, 0}, {active, false}, {reuseaddr, true}]).
% Call echo:listen(Port) to start the service.
listen(Port) ->
{ok, LSocket} = gen_tcp:listen(Port, ?TCP_OPTIONS),
accept(LSocket).
% Wait for incoming connections and spawn the echo loop when we get one.
accept(LSocket) ->
{ok, Socket} = gen_tcp:accept(LSocket),
spawn(fun() -> loop(Socket) end),
accept(LSocket).
% Echo back whatever data we receive on Socket.
loop(Socket) ->
case gen_tcp:recv(Socket, 0) of
{ok, Data} ->
case Data of
"Hello" -> gen_tcp:send(Socket, "coi\n");
"Hello\n" -> gen_tcp:send(Socket, "coi\n");
'Hello' -> gen_tcp:send(Socket, "coi\n");
<<"Hello">> -> gen_tcp:send(Socket, "coi\n");
<<"Hello\n">> -> gen_tcp:send(Socket, "coi\n");
_ -> gen_tcp:send(Socket, "I don't understand")
end,
loop(Socket);
{error, closed} ->
ok
end.
My current test is to open two terminal windows and execute
[CONSOLE 1]
erl
c(translate).
translate:listen(8888).
[CONSOLE 2]
telnet localhost 8888
whatever
Hello
And the output becomes:
I don't understand
I don't understand
How can I parse the incoming data? This style of pattern matching seems to be failing completely. Thanks!
Try this one:
case binary_to_list(Data) of
"Hello\r\n" -> gen_tcp:send(Socket, "this will be good variant\n");
_ -> gen_tcp:send(Socket, "I don't understand")
end,
Or without explicit convert:
case Data of
<<"Hello\r\n">> -> gen_tcp:send(Socket, "this will be good variant\n");
_ -> gen_tcp:send(Socket, "I don't understand")
end,
Updated from comments
To work with more complicated matching remove "\r\n" suffix first:
Content = list_to_binary(lists:subtract(binary_to_list(Data), "\r\n")),
case Content of
<<"Hello">> -> gen_tcp:send(Socket, <<"Good day!\n">>);
<<"My name is, ", Name/binary>> -> gen_tcp:send(Socket, <<"Hello ", Name/binary, "!\n">>);
_ -> gen_tcp:send(Socket, "I don't understand")
end,