anchor.diff:      Date = {
         {
           list_to_integer(binary_to_list(Year)) + 2000,
  -        list_to_integer(binary_to_list(Month)), 
  +        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(Hour)),
           list_to_integer(binary_to_list(Minute)),
  -        list_to_integer(binary_to_list(Second)) 
  +        list_to_integer(binary_to_list(Second))
         }
        },
  -  em_helper_time:datetime_to_utc(Date).
  +    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)).
  +    {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;
  
positive.code: 
  %% (123456789012 BP05 123456789012345 120101 A 6000.0000N 13000.0000E 000.0 120200 000.00 00000000 L000946BB)
  test() ->
    Packet = <<"(123456789012BP05123456789012345120101A6000.0000N13000.0000E000.0120200000.0000000000L000946BB)">>,
    {ok, Pattern} = re:compile(?PATTERN),
    parse(Packet, Pattern).
  
negative.code: -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
          >>
         ).
  
  
  -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}.
  
  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}).
  
  
  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, 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
  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] ->
              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.
  