anchor.diff:    {noreply, NewState :: #state{}} |
     {noreply, NewState :: #state{}, timeout() | hibernate} |
     {stop, Reason :: term(), NewState :: #state{}}).
  -handle_info({command, Command}, State=#state{socket=Socket, transport=Transport}) ->
  +handle_info({command, Command}, State) ->
  +  do_execute_command(State, Command);
  +handle_info({tcp, _, ?TRASH}, State = #state{socket = Socket, transport = Transport}) ->
  +  Transport:setopts(Socket, [{active, once}]),
  +  {noreply, State, ?TIMEOUT};
  +handle_info({tcp, Socket, Data}, State = #state{socket = Socket, transport = Transport}) when byte_size(Data) > 1 ->
  +  Transport:setopts(Socket, [{active, once}]),
  +  do_process_data(State, Data);
  +handle_info({tcp_closed, _Socket}, State) ->
  +  {stop, normal, State};
  +handle_info({tcp_error, _, Reason}, State) ->
  +  {stop, Reason, State};
  +handle_info(timeout, State) ->
  +  {stop, normal, State};
  +handle_info(_Info, State) ->
  +  {stop, normal, State}.
  +%%handle_info(_Info, State) ->
  +%%  {noreply, State}.
  +
  +%%--------------------------------------------------------------------
  +%% @private
  +%% @doc
  +%% This function is called by a gen_server when it is about to
  +%% terminate. It should be the opposite of Module:init/1 and do any
  +%% necessary cleaning up. When it returns, the gen_server terminates
  +%% with Reason. The return value is ignored.
  +%%
  +%% @spec terminate(Reason, State) -> void()
  +%% @end
  +%%--------------------------------------------------------------------
  +-spec(terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()),
  +    State :: #state{}) -> term()).
  +terminate(_Reason, _State) ->
  +  ok.
  +
  +%%--------------------------------------------------------------------
  +%% @private
  +%% @doc
  +%% Convert process state when code is changed
  +%%
  +%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
  +%% @end
  +%%--------------------------------------------------------------------
  +-spec(code_change(OldVsn :: term() | {down, term()}, State :: #state{},
  +    Extra :: term()) ->
  +  {ok, NewState :: #state{}} | {error, Reason :: term()}).
  +code_change(_OldVsn, State, _Extra) ->
  +  {ok, State}.
  +
  +%%%===================================================================
  +%%% Internal functions
  +%%%===================================================================
  +do_execute_command(State = #state{transport = Transport, socket = Socket}, Command) ->
     case em_manager_devices:get_by_id(Command#command.deviceId) of
       {ok, Device} ->
         case do_encode_command(Device#device.uniqueId, Command) of
  
positive.code:     {error, Reason} ->
        em_logger:info("Error: ~s", [Reason]),
        {noreply, State, ?TIMEOUT}
    end;
  handle_info({tcp, _, ?TRASH}, State=#state{socket=Socket, transport=Transport}) ->
    Transport:setopts(Socket, [{active, once}]),
    {noreply, State, ?TIMEOUT};
  handle_info({tcp, Socket, Data}, State=#state{protocol = Protocol, socket=Socket, transport=Transport, deviceId = 0}) when byte_size(Data) > 1 ->
    Transport:setopts(Socket, [{active, once}]),
  
    em_logger:info("[packet] unit: ip = '~s' data: ~s", [em_hardware:resolve(Socket), Data]),
    case parse(Data) of
      {ok, Imei, PositionModel} ->
        em_logger:info("[packet] unit: ip = '~s' imei = '~s' message: ~w", [em_hardware:resolve(Socket), Imei, PositionModel]),
        case em_data_manager:get_device_by_uid(Imei) of
          {error, _Reason} ->
            em_logger:info("[packet] unit: ip = '~s' unknown device with imei = '~s'", [em_hardware:resolve(Socket), Imei]),
            {stop, normal, State};
          {ok, Object} ->
            em_proc:registry(Object#device.id, self()),
            Position = PositionModel#position{
              deviceId = Object#device.id,
              protocol = atom_to_binary(Protocol, utf8),
              attributes = maps:merge(PositionModel#position.attributes, #{
                ?KEY_IP => em_hardware:resolve(Socket)
              })
            },
            em_logger:info("save message => unit: ip = '~s' id = '~w' imei = '~s' position: ~w", [em_hardware:resolve(Socket), Object#device.id, Imei, Position]),
            em_data_manager:create_position(Object#device.id, Position),
            {noreply, State#state{deviceId = Object#device.id}, ?TIMEOUT}
        end;
      {error, Message} ->
        em_logger:info("ERROR: ~s", [Message]),
        {stop, normal, State}
    end;
  handle_info({tcp, Socket, Data}, State=#state{protocol = Protocol, socket=Socket, transport=Transport, deviceId = DeviceId}) when byte_size(Data) > 1 ->
    Transport:setopts(Socket, [{active, once}]),
  
    em_logger:info("[packet] unit: ip = '~s' data: ~s", [em_hardware:resolve(Socket), Data]),
    case parse(Data) of
      {ok, Imei, PositionModel} ->
        em_logger:info("[packet] unit: ip = '~s' imei = '~s' message: ~w", [em_hardware:resolve(Socket), Imei, PositionModel]),
        Position = PositionModel#position{
          deviceId = DeviceId,
          protocol = atom_to_binary(Protocol, utf8),
          attributes = maps:merge(PositionModel#position.attributes, #{
            ?KEY_IP => em_hardware:resolve(Socket)
          })
        },
        em_logger:info("save message => unit: ip = '~s' id = '~w' imei = '~s' position: ~w", [em_hardware:resolve(Socket), DeviceId, Imei, Position]),
        em_data_manager:create_position(DeviceId, Position),
        {noreply, State, ?TIMEOUT};
      {error, Message} ->
        em_logger:info("ERROR: ~s", [Message]),
        {stop, normal, State}
    end;
  handle_info({tcp_closed, _Socket}, State) ->
    {stop, normal, State};
  handle_info({tcp_error, _, Reason}, State) ->
    {stop, Reason, State};
  handle_info(timeout, State) ->
    {stop, normal, State};
  handle_info(_Info, State) ->
    {stop, normal, State}.
  %%handle_info(_Info, State) ->
  %%  {noreply, State}.
  
  %%--------------------------------------------------------------------
  %% @private
  %% @doc
  %% This function is called by a gen_server when it is about to
  %% terminate. It should be the opposite of Module:init/1 and do any
  %% necessary cleaning up. When it returns, the gen_server terminates
  %% with Reason. The return value is ignored.
  %%
  %% @spec terminate(Reason, State) -> void()
  %% @end
  %%--------------------------------------------------------------------
  -spec(terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()),
      State :: #state{}) -> term()).
  terminate(_Reason, _State) ->
    ok.
  
  %%--------------------------------------------------------------------
  %% @private
  %% @doc
  %% Convert process state when code is changed
  %%
  %% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
  %% @end
  %%--------------------------------------------------------------------
  -spec(code_change(OldVsn :: term() | {down, term()}, State :: #state{},
      Extra :: term()) ->
    {ok, NewState :: #state{}} | {error, Reason :: term()}).
  code_change(_OldVsn, State, _Extra) ->
    {ok, State}.
  
  %%%===================================================================
  %%% Internal functions
  %%%===================================================================
  do_encode_command(UniqueId, #command{type = ?TYPE_POSITION_SINGLE}) ->
    {ok, do_format_command(UniqueId, <<"Q">>, <<"A10">>)};
  do_encode_command(UniqueId, #command{type = ?TYPE_ENGINE_STOP}) ->
  
negative.code: 
  do_format_command(UniqueId, DataId, Content) ->
    Length = 1 + byte_size(UniqueId) + 1 + byte_size(Content) + 5,
    Result = list_to_binary(io_lib:format("@@~s~w,~s,~s*", [DataId, Length, UniqueId, Content])),
    CheckSum = em_checksum:sum(Result),
    list_to_binary(io_lib:format("~s~2.16.0B\r\n", [Result, CheckSum])).
  
  
  do_process_data(State = #state{transport = _Transport, socket = Socket, protocol = Protocol, deviceId = 0}, Data) ->
    em_logger:info("[packet] unit: ip = '~s' data: ~s", [em_hardware:resolve(Socket), Data]),
    case parse(Data) of
      {ok, Imei, PositionModel} ->
        em_logger:info("[packet] unit: ip = '~s' imei = '~s' message: ~w", [em_hardware:resolve(Socket), Imei, PositionModel]),
        case em_data_manager:get_device_by_uid(Imei) of
          {error, _Reason} ->
            em_logger:info("[packet] unit: ip = '~s' unknown device with imei = '~s'", [em_hardware:resolve(Socket), Imei]),
            {stop, normal, State};
          {ok, Object} ->
            em_proc:registry(Object#device.id, self()),
            do_save_position(State#state{deviceId = Object#device.id}, Object#device.id, Imei, PositionModel)
        end;
      {error, Message} ->
        em_logger:info("ERROR: ~s", [Message]),
        {stop, normal, State}
    end;
  do_process_data(State = #state{transport = _Transport, socket = Socket, protocol = Protocol, deviceId = DeviceId}, Data) ->
    em_logger:info("[packet] unit: ip = '~s' data: ~s", [em_hardware:resolve(Socket), Data]),
    case parse(Data) of
      {ok, Imei, PositionModel} ->
        do_save_position(State, DeviceId, Imei, PositionModel);
      {error, Message} ->
        em_logger:info("ERROR: ~s", [Message]),
        {stop, normal, State}
    end.
  
  do_save_position(State = #state{socket = Socket, protocol = Protocol}, DeviceId, Imei, PositionModel) ->
    em_logger:info("[packet] unit: ip = '~s' imei = '~s' message: ~w", [em_hardware:resolve(Socket), Imei, PositionModel]),
    Position = PositionModel#position{
      deviceId = DeviceId,
      protocol = atom_to_binary(Protocol, utf8),
      attributes = maps:merge(PositionModel#position.attributes, #{
        ?KEY_IP => em_hardware:resolve(Socket)
      })
    },
    em_logger:info("save message => unit: ip = '~s' id = '~w' imei = '~s' position: ~w", [em_hardware:resolve(Socket), DeviceId, Imei, Position]),
    em_data_manager:create_position(DeviceId, Position),
    {noreply, State, ?TIMEOUT}.
  
  %% echo "20. meitrack"
  %% (echo -n -e "\$\$d138,123456789012345,AAA,35,60.000000,130.000000,120101122000,A,7,18,0,0,0,49,3800,24965,510|10|0081|4F4F,0000,000D|0010|0012|0963|0000,,*BF\r\n";) | nc -v localhost 5020
  