anchor.diff:  -define(Millage, <<"(?:L([0-9a-fA-F]+))?">>).       % Millage
   
   -define(PATTERN,
  -  <<
  -  ?DeviceID/binary,
  -  ?Command/binary,
  -  ?IMEI/binary,
  -  ?Date/binary,
  -  ?Validity/binary,
  -  ?Latitude/binary,
  -  ?LatitudeType/binary,
  -  ?Longitude/binary,
  -  ?LongitudeType/binary,
  -  ?Speed/binary,
  -  ?Time/binary,
  -  ?Course/binary,
  -  ?State/binary,
  -  ?Millage/binary
  -  >>
  -).
  +        <<
  +          ?DeviceID/binary,
  +          ?Command/binary,
  +          ?IMEI/binary,
  +          ?Date/binary,
  +          ?Validity/binary,
  +          ?Latitude/binary,
  +          ?LatitudeType/binary,
  +          ?Longitude/binary,
  +          ?LongitudeType/binary,
  +          ?Speed/binary,
  +          ?Time/binary,
  +          ?Course/binary,
  +          ?State/binary,
  +          ?Millage/binary
  +        >>
  +       ).
   
   
   -record(state, {protocol, transport, socket, timeout, device, pattern}).
   
   start_link(Ref, Socket, Transport, Opts) ->
  -  Pid = spawn_link(?MODULE, init, [Ref, Socket, Transport, Opts]),
  -  {ok, Pid}.
  +    Pid = spawn_link(?MODULE, init, [Ref, Socket, Transport, Opts]),
  +    {ok, Pid}.
   
   init(Ref, Socket, Transport, Opts) ->
  -  ok = ranch:accept_ack(Ref),
  -  Protocol = proplists:get_value(protocol, Opts),
  -  {ok, Pattern} = re:compile(?PATTERN),
  -  loop(#state{protocol = Protocol, socket = Socket, transport = Transport, pattern = Pattern}).
  +    ok = ranch:accept_ack(Ref),
  +    Protocol = proplists:get_value(protocol, Opts),
  +    {ok, Pattern} = re:compile(?PATTERN),
  +    loop(#state{protocol = Protocol, socket = Socket, transport = Transport, pattern = Pattern}).
   
   
   loop(State = #state{protocol = Protocol, socket = Socket, transport = Transport, pattern = Pattern}) ->
  -  case Transport:recv(Socket, 0, 5000) of
  -    {ok, Data} ->
  -      em_logger:info("[packet] unit: ip = '~s' data: ~s", [em_hardware:resolve(Socket), Data]),
  -      {Imei, Message} = parse(Data, Pattern),
  -      case em_data_manager:get_device_by_uid(Imei) of
  -        null ->
  -          em_logger:info("[packet] unit: ip = '~s' unknown device with imei = '~s'", [em_hardware:resolve(Socket), Imei]),
  -          Transport:close(Socket);
  -        Object ->
  -          em_logger:info("save message => unit: ip = '~s' id = '~w' imei = '~s' message: ~s", [em_hardware:resolve(Socket), maps:get(<<"id">>, Object), Imei, em_json:encode(Message)]),
  -          em_data_manager:create_message(maps:get(<<"id">>, Object), Protocol, maps:merge(#{imei => maps:get(<<"uniqueId">>, Object)}, Message)),
  -          loop(State#state{device = Object})
  -      end;
  -    _ ->
  -      Transport:close(Socket)
  -  end.
  +    case Transport:recv(Socket, 0, 5000) of
  +        {ok, Data} ->
  +            em_logger:info("[packet] unit: ip = '~s' data: ~s", [em_hardware:resolve(Socket), Data]),
  +            {Imei, PositionModel} = parse(Data, Pattern),
  +            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]),
  +                    Transport:close(Socket);
  +                {ok, Object} ->
  +                    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, Position),
  +                    loop(State#state{device = Object})
  +            end;
  +        _ ->
  +            Transport:close(Socket)
  +    end.
   
  -% echo "2. tk103"
  -% (echo -n -e "(123456789012BP05123456789012345120101A6000.0000N13000.0000E000.0120200000.0000000000L000946BB)";) | nc -v localhost 5002
  +%% echo "2. tk103"
  +%% (echo -n -e "(123456789012BP05123456789012345120101A6000.0000N13000.0000E000.0120200000.0000000000L000946BB)";) | nc -v localhost 5002
   parse(Data, Pattern) ->
       case data_match(Data, Pattern) of
  -        [_, DeviceId, Command, Imei, Year, Month, Day, Validity, LatDD, LatMM_MMMM, LatType, LonDD, LonMM_MMMM, LonType, Speed, Hour, Minute, Second, Course, State, Millage] ->
  -            Message = #{
  -              <<"deviceTime">> => parse_date(Year, Month, Day, Hour, Minute, Second),
  -              <<"latitude">> => parse_coord(LatDD, LatMM_MMMM, LatType),
  -              <<"longitude">> => parse_coord(LonDD, LonMM_MMMM, LonType),
  -              <<"speed">> => parse_speed(Speed),
  -              <<"course">> => parse_course(Course),
  -              <<"deviceId">> => parse_device_id(DeviceId),
  -              <<"valid">> => parse_validity(Validity)
  -             },
  -            {Imei, Message};
  +        [_, _DeviceId, _Command, Imei, Year, Month, Day, Validity, LatDD, LatMM_MMMM, LatType, LonDD, LonMM_MMMM, LonType, Speed, Hour, Minute, Second, Course, _State, _Millage] ->
  +            Position = #position{
  +                          deviceTime = parse_date(Year, Month, Day, Hour, Minute, Second),
  +                          latitude = parse_coord(LatDD, LatMM_MMMM, LatType),
  +                          longitude = parse_coord(LonDD, LonMM_MMMM, LonType),
  +                          speed = parse_speed(Speed),
  +                          course = parse_course(Course),
  +                          valid = parse_validity(Validity)
  +                         },
  +            {Imei, Position};
           _ ->
               #{}
       end.
  
positive.code:     Date = {
        {
          list_to_integer(binary_to_list(Year)) + 2000,
          list_to_integer(binary_to_list(Month)), 
          list_to_integer(binary_to_list(Day))
        },
        {
          list_to_integer(binary_to_list(Hour)), 
          list_to_integer(binary_to_list(Minute)),
          list_to_integer(binary_to_list(Second)) 
        }
       },
    em_helper_time:datetime_to_utc(Date).
  
  data_match(Data, Pattern) ->
    {match, List} = re:run(Data, Pattern),
    lists:reverse(lists:foldl(fun(Param, Res) ->
                                      [read_param(Param, Data) | Res]
                              end, [], List)).
  
  read_param({-1, 0}, _) ->
      void;
  
negative.code: 
  -behaviour(ranch_protocol).
  
  -include("em_records.hrl").
  
  %% API
  -export([start_link/4]).
  -export([init/4]).
  