diff options
Diffstat (limited to 'src/org/traccar')
89 files changed, 1692 insertions, 288 deletions
diff --git a/src/org/traccar/DistanceHandler.java b/src/org/traccar/DistanceHandler.java index 1586d116b..c737457f6 100644 --- a/src/org/traccar/DistanceHandler.java +++ b/src/org/traccar/DistanceHandler.java @@ -1,5 +1,6 @@ /* * Copyright 2015 Amila Silva + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,11 +41,13 @@ public class DistanceHandler extends BaseDataHandler { distance = ((Number) last.getAttributes().get(Event.KEY_DISTANCE)).doubleValue(); } - distance += DistanceCalculator.distance( - position.getLatitude(), position.getLongitude(), - last.getLatitude(), last.getLongitude()); + if (position.getValid()) { + distance += DistanceCalculator.distance( + position.getLatitude(), position.getLongitude(), + last.getLatitude(), last.getLongitude()); - distance = BigDecimal.valueOf(distance).setScale(2, RoundingMode.HALF_EVEN).doubleValue(); + distance = BigDecimal.valueOf(distance).setScale(2, RoundingMode.HALF_EVEN).doubleValue(); + } } position.set(Event.KEY_DISTANCE, distance); diff --git a/src/org/traccar/MainEventHandler.java b/src/org/traccar/MainEventHandler.java index f0ef36e5b..f99765de4 100644 --- a/src/org/traccar/MainEventHandler.java +++ b/src/org/traccar/MainEventHandler.java @@ -48,7 +48,10 @@ public class MainEventHandler extends IdleStateAwareChannelHandler { s.append("course: ").append(String.format("%.1f", position.getCourse())); Log.info(s.toString()); - Context.getConnectionManager().updatePosition(position); + Position lastPosition = Context.getConnectionManager().getLastPosition(position.getDeviceId()); + if (lastPosition == null || position.getFixTime().compareTo(lastPosition.getFixTime()) > 0) { + Context.getConnectionManager().updatePosition(position); + } } } diff --git a/src/org/traccar/api/AsyncSocket.java b/src/org/traccar/api/AsyncSocket.java index 7de182d7e..c289367ea 100644 --- a/src/org/traccar/api/AsyncSocket.java +++ b/src/org/traccar/api/AsyncSocket.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,7 @@ public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.U private Collection<Long> devices; public AsyncSocket(long userId) { - devices = Context.getPermissionsManager().allowedDevices(userId); + devices = Context.getPermissionsManager().getDevicePermissions(userId); } @Override @@ -66,7 +66,7 @@ public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.U } private void sendData(String key, Collection<?> data) { - if (!data.isEmpty()) { + if (!data.isEmpty() && isConnected()) { JsonObjectBuilder json = Json.createObjectBuilder(); json.add(key, JsonConverter.arrayToJson(data)); getRemote().sendString(json.build().toString(), null); diff --git a/src/org/traccar/api/AsyncSocketServlet.java b/src/org/traccar/api/AsyncSocketServlet.java index fbfe248e5..ef6cef732 100644 --- a/src/org/traccar/api/AsyncSocketServlet.java +++ b/src/org/traccar/api/AsyncSocketServlet.java @@ -20,6 +20,7 @@ import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse; import org.eclipse.jetty.websocket.servlet.WebSocketCreator; import org.eclipse.jetty.websocket.servlet.WebSocketServlet; import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; +import org.traccar.Context; import org.traccar.api.resource.SessionResource; public class AsyncSocketServlet extends WebSocketServlet { @@ -28,12 +29,16 @@ public class AsyncSocketServlet extends WebSocketServlet { @Override public void configure(WebSocketServletFactory factory) { - factory.getPolicy().setIdleTimeout(ASYNC_TIMEOUT); + factory.getPolicy().setIdleTimeout(Context.getConfig().getLong("web.timeout", ASYNC_TIMEOUT)); factory.setCreator(new WebSocketCreator() { @Override public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp) { - long userId = (Long) req.getSession().getAttribute(SessionResource.USER_ID_KEY); - return new AsyncSocket(userId); + if (req.getSession() != null) { + long userId = (Long) req.getSession().getAttribute(SessionResource.USER_ID_KEY); + return new AsyncSocket(userId); + } else { + return null; + } } }); } diff --git a/src/org/traccar/api/CorsResponseFilter.java b/src/org/traccar/api/CorsResponseFilter.java index 001f6ab4c..178d08812 100644 --- a/src/org/traccar/api/CorsResponseFilter.java +++ b/src/org/traccar/api/CorsResponseFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,8 +19,6 @@ import org.jboss.netty.handler.codec.http.HttpHeaders; import org.traccar.Context; import java.io.IOException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.container.ContainerResponseFilter; @@ -37,7 +35,7 @@ public class CorsResponseFilter implements ContainerResponseFilter { public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS_VALUE = "true"; public static final String ACCESS_CONTROL_ALLOW_METHODS_KEY = "Access-Control-Allow-Methods"; - public static final String ACCESS_CONTROL_ALLOW_METHODS_VALUE = "GET, POST, PUT, DELETE"; + public static final String ACCESS_CONTROL_ALLOW_METHODS_VALUE = "GET, POST, PUT, DELETE, OPTIONS"; @Override public void filter(ContainerRequestContext request, ContainerResponseContext response) throws IOException { @@ -54,11 +52,11 @@ public class CorsResponseFilter implements ContainerResponseFilter { if (!response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN_KEY)) { String origin = request.getHeaderString(HttpHeaders.Names.ORIGIN); String allowed = Context.getConfig().getString("web.origin"); - if (allowed == null) { + + if (allowed == null || origin == null) { response.getHeaders().add(ACCESS_CONTROL_ALLOW_ORIGIN_KEY, ACCESS_CONTROL_ALLOW_ORIGIN_VALUE); - } else if (allowed.contains(origin)) { - String originSafe = URLEncoder.encode(origin, StandardCharsets.UTF_8.name()); - response.getHeaders().add(ACCESS_CONTROL_ALLOW_ORIGIN_KEY, originSafe); + } else if (allowed.equals(ACCESS_CONTROL_ALLOW_ORIGIN_VALUE) || allowed.contains(origin)) { + response.getHeaders().add(ACCESS_CONTROL_ALLOW_ORIGIN_KEY, origin); } } } diff --git a/src/org/traccar/api/SecurityRequestFilter.java b/src/org/traccar/api/SecurityRequestFilter.java index 20186b0cb..4c6137ede 100644 --- a/src/org/traccar/api/SecurityRequestFilter.java +++ b/src/org/traccar/api/SecurityRequestFilter.java @@ -55,6 +55,11 @@ public class SecurityRequestFilter implements ContainerRequestFilter { @Override public void filter(ContainerRequestContext requestContext) { + + if (requestContext.getMethod().equals("OPTIONS")) { + throw new WebApplicationException(Response.status(Response.Status.OK).build()); + } + SecurityContext securityContext = null; String authHeader = requestContext.getHeaderString(AUTHORIZATION_HEADER); diff --git a/src/org/traccar/api/resource/PermissionResource.java b/src/org/traccar/api/resource/DevicePermissionResource.java index 50deb77c2..f77375cba 100644 --- a/src/org/traccar/api/resource/PermissionResource.java +++ b/src/org/traccar/api/resource/DevicePermissionResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ package org.traccar.api.resource; import org.traccar.Context; import org.traccar.api.BaseResource; -import org.traccar.model.Permission; +import org.traccar.model.DevicePermission; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -28,13 +28,13 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.sql.SQLException; -@Path("permissions") +@Path("permissions/devices") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) -public class PermissionResource extends BaseResource { +public class DevicePermissionResource extends BaseResource { @POST - public Response add(Permission entity) throws SQLException { + public Response add(DevicePermission entity) throws SQLException { Context.getPermissionsManager().checkAdmin(getUserId()); Context.getDataManager().linkDevice(entity.getUserId(), entity.getDeviceId()); Context.getPermissionsManager().refresh(); @@ -42,7 +42,7 @@ public class PermissionResource extends BaseResource { } @DELETE - public Response remove(Permission entity) throws SQLException { + public Response remove(DevicePermission entity) throws SQLException { Context.getPermissionsManager().checkAdmin(getUserId()); Context.getDataManager().unlinkDevice(entity.getUserId(), entity.getDeviceId()); Context.getPermissionsManager().refresh(); diff --git a/src/org/traccar/api/resource/GroupPermissionResource.java b/src/org/traccar/api/resource/GroupPermissionResource.java new file mode 100644 index 000000000..b8ec4ae3c --- /dev/null +++ b/src/org/traccar/api/resource/GroupPermissionResource.java @@ -0,0 +1,52 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.api.resource; + +import org.traccar.Context; +import org.traccar.api.BaseResource; +import org.traccar.model.GroupPermission; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.sql.SQLException; + +@Path("permissions/groups") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class GroupPermissionResource extends BaseResource { + + @POST + public Response add(GroupPermission entity) throws SQLException { + Context.getPermissionsManager().checkAdmin(getUserId()); + Context.getDataManager().linkGroup(entity.getUserId(), entity.getGroupId()); + Context.getPermissionsManager().refresh(); + return Response.ok(entity).build(); + } + + @DELETE + public Response remove(GroupPermission entity) throws SQLException { + Context.getPermissionsManager().checkAdmin(getUserId()); + Context.getDataManager().unlinkGroup(entity.getUserId(), entity.getGroupId()); + Context.getPermissionsManager().refresh(); + return Response.noContent().build(); + } + +} diff --git a/src/org/traccar/api/resource/GroupResource.java b/src/org/traccar/api/resource/GroupResource.java new file mode 100644 index 000000000..49f839499 --- /dev/null +++ b/src/org/traccar/api/resource/GroupResource.java @@ -0,0 +1,85 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.api.resource; + +import org.traccar.Context; +import org.traccar.api.BaseResource; +import org.traccar.model.Device; +import org.traccar.model.Group; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.sql.SQLException; +import java.util.Collection; + +@Path("groups") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class GroupResource extends BaseResource { + + @GET + public Collection<Group> get( + @QueryParam("all") boolean all, @QueryParam("userId") long userId) throws SQLException { + if (all) { + Context.getPermissionsManager().checkAdmin(getUserId()); + return Context.getDataManager().getAllGroups(); + } else { + if (userId == 0) { + userId = getUserId(); + } + Context.getPermissionsManager().checkUser(getUserId(), userId); + return Context.getDataManager().getGroups(userId); + } + } + + @POST + public Response add(Group entity) throws SQLException { + Context.getPermissionsManager().checkReadonly(getUserId()); + Context.getDataManager().addGroup(entity); + Context.getDataManager().linkGroup(getUserId(), entity.getId()); + Context.getPermissionsManager().refresh(); + return Response.ok(entity).build(); + } + + @Path("{id}") + @PUT + public Response update(@PathParam("id") long id, Group entity) throws SQLException { + Context.getPermissionsManager().checkReadonly(getUserId()); + Context.getPermissionsManager().checkGroup(getUserId(), id); + Context.getDataManager().updateGroup(entity); + return Response.ok(entity).build(); + } + + @Path("{id}") + @DELETE + public Response remove(@PathParam("id") long id) throws SQLException { + Context.getPermissionsManager().checkReadonly(getUserId()); + Context.getPermissionsManager().checkGroup(getUserId(), id); + Context.getDataManager().removeGroup(id); + Context.getPermissionsManager().refresh(); + return Response.noContent().build(); + } + +} diff --git a/src/org/traccar/api/resource/PositionResource.java b/src/org/traccar/api/resource/PositionResource.java index ec6925b3a..9c174625c 100644 --- a/src/org/traccar/api/resource/PositionResource.java +++ b/src/org/traccar/api/resource/PositionResource.java @@ -38,9 +38,14 @@ public class PositionResource extends BaseResource { public Collection<Position> get( @QueryParam("deviceId") long deviceId, @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException { - Context.getPermissionsManager().checkDevice(getUserId(), deviceId); - return Context.getDataManager().getPositions( - deviceId, JsonConverter.parseDate(from), JsonConverter.parseDate(to)); + if (deviceId == 0) { + return Context.getConnectionManager().getInitialState( + Context.getPermissionsManager().getDevicePermissions(getUserId())); + } else { + Context.getPermissionsManager().checkDevice(getUserId(), deviceId); + return Context.getDataManager().getPositions( + deviceId, JsonConverter.parseDate(from), JsonConverter.parseDate(to)); + } } } diff --git a/src/org/traccar/database/DataManager.java b/src/org/traccar/database/DataManager.java index 73984952e..d2da4e701 100644 --- a/src/org/traccar/database/DataManager.java +++ b/src/org/traccar/database/DataManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2015 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2012 - 2016 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import java.sql.SQLException; +import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; @@ -36,10 +37,13 @@ import liquibase.exception.LiquibaseException; import liquibase.resource.FileSystemResourceAccessor; import liquibase.resource.ResourceAccessor; import org.traccar.Config; +import org.traccar.Context; import org.traccar.helper.Log; import org.traccar.model.Device; +import org.traccar.model.Group; +import org.traccar.model.GroupPermission; import org.traccar.model.MiscFormatter; -import org.traccar.model.Permission; +import org.traccar.model.DevicePermission; import org.traccar.model.Position; import org.traccar.model.Server; import org.traccar.model.User; @@ -53,10 +57,14 @@ public class DataManager implements IdentityManager { private DataSource dataSource; + private final long dataRefreshDelay; + private final Map<Long, Device> devicesById = new HashMap<>(); private final Map<String, Device> devicesByUniqueId = new HashMap<>(); private long devicesLastUpdate; - private final long devicesRefreshDelay; + + private final Map<Long, Group> groupsById = new HashMap<>(); + private long groupsLastUpdate; public DataManager(Config config) throws Exception { this.config = config; @@ -64,7 +72,7 @@ public class DataManager implements IdentityManager { initDatabase(); initDatabaseSchema(); - devicesRefreshDelay = config.getLong("database.refreshDelay", DEFAULT_REFRESH_DELAY) * 1000; + dataRefreshDelay = config.getLong("database.refreshDelay", DEFAULT_REFRESH_DELAY) * 1000; } public DataSource getDataSource() { @@ -112,7 +120,7 @@ public class DataManager implements IdentityManager { } private void updateDeviceCache(boolean force) throws SQLException { - if (System.currentTimeMillis() - devicesLastUpdate > devicesRefreshDelay || force) { + if (System.currentTimeMillis() - devicesLastUpdate > dataRefreshDelay || force) { devicesById.clear(); devicesByUniqueId.clear(); for (Device device : getAllDevices()) { @@ -140,6 +148,25 @@ public class DataManager implements IdentityManager { return devicesByUniqueId.get(uniqueId); } + private void updateGroupCache(boolean force) throws SQLException { + if (System.currentTimeMillis() - groupsLastUpdate > dataRefreshDelay || force) { + groupsById.clear(); + for (Group group : getAllGroups()) { + groupsById.put(group.getId(), group); + } + groupsLastUpdate = System.currentTimeMillis(); + } + } + + public Group getGroupById(long id) { + try { + updateGroupCache(!groupsById.containsKey(id)); + } catch (SQLException e) { + Log.warning(e); + } + return groupsById.get(id); + } + private String getQuery(String key) { String query = config.getString(key); if (query == null) { @@ -221,9 +248,14 @@ public class DataManager implements IdentityManager { .executeUpdate(); } - public Collection<Permission> getPermissions() throws SQLException { - return QueryBuilder.create(dataSource, getQuery("database.getPermissionsAll")) - .executeQuery(Permission.class); + public Collection<DevicePermission> getDevicePermissions() throws SQLException { + return QueryBuilder.create(dataSource, getQuery("database.selectDevicePermissions")) + .executeQuery(DevicePermission.class); + } + + public Collection<GroupPermission> getGroupPermissions() throws SQLException { + return QueryBuilder.create(dataSource, getQuery("database.selectGroupPermissions")) + .executeQuery(GroupPermission.class); } public Collection<Device> getAllDevices() throws SQLException { @@ -232,9 +264,11 @@ public class DataManager implements IdentityManager { } public Collection<Device> getDevices(long userId) throws SQLException { - return QueryBuilder.create(dataSource, getQuery("database.selectDevices")) - .setLong("userId", userId) - .executeQuery(Device.class); + Collection<Device> devices = new ArrayList<>(); + for (long id : Context.getPermissionsManager().getDevicePermissions(userId)) { + devices.add(getDeviceById(id)); + } + return devices; } public void addDevice(Device device) throws SQLException { @@ -286,6 +320,51 @@ public class DataManager implements IdentityManager { AsyncServlet.sessionRefreshUser(userId); } + public Collection<Group> getAllGroups() throws SQLException { + return QueryBuilder.create(dataSource, getQuery("database.selectGroupsAll")) + .executeQuery(Group.class); + } + + public Collection<Group> getGroups(long userId) throws SQLException { + Collection<Group> groups = new ArrayList<>(); + for (long id : Context.getPermissionsManager().getGroupPermissions(userId)) { + groups.add(getGroupById(id)); + } + return groups; + } + + public void addGroup(Group group) throws SQLException { + group.setId(QueryBuilder.create(dataSource, getQuery("database.insertGroup"), true) + .setObject(group) + .executeUpdate()); + } + + public void updateGroup(Group group) throws SQLException { + QueryBuilder.create(dataSource, getQuery("database.updateGroup")) + .setObject(group) + .executeUpdate(); + } + + public void removeGroup(long groupId) throws SQLException { + QueryBuilder.create(dataSource, getQuery("database.deleteGroup")) + .setLong("id", groupId) + .executeUpdate(); + } + + public void linkGroup(long userId, long groupId) throws SQLException { + QueryBuilder.create(dataSource, getQuery("database.linkGroup")) + .setLong("userId", userId) + .setLong("groupId", groupId) + .executeUpdate(); + } + + public void unlinkGroup(long userId, long groupId) throws SQLException { + QueryBuilder.create(dataSource, getQuery("database.unlinkGroup")) + .setLong("userId", userId) + .setLong("groupId", groupId) + .executeUpdate(); + } + public Collection<Position> getPositions(long deviceId, Date from, Date to) throws SQLException { return QueryBuilder.create(dataSource, getQuery("database.selectPositions")) .setLong("deviceId", deviceId) diff --git a/src/org/traccar/database/GroupTree.java b/src/org/traccar/database/GroupTree.java new file mode 100644 index 000000000..b383b1501 --- /dev/null +++ b/src/org/traccar/database/GroupTree.java @@ -0,0 +1,153 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.database; + +import org.traccar.model.Device; +import org.traccar.model.Group; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class GroupTree { + + private static class TreeNode { + + private Group group; + private Device device; + private Collection<TreeNode> children = new HashSet<>(); + + public TreeNode(Group group) { + this.group = group; + } + + public TreeNode(Device device) { + this.device = device; + } + + @Override + public int hashCode() { + if (group != null) { + return (int) group.getId(); + } else { + return (int) device.getId(); + } + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof TreeNode)) { + return false; + } + TreeNode other = (TreeNode) obj; + if (other == this) { + return true; + } + if (group != null) { + if (other.group != null) { + return group.getId() == other.group.getId(); + } + } else if (device != null) { + if (other.device != null) { + return device.getId() == other.device.getId(); + } + } + return false; + } + + public Group getGroup() { + return group; + } + + public Device getDevice() { + return device; + } + + public void setParent(TreeNode parent) { + if (parent != null) { + parent.children.add(this); + } + } + + public Collection<TreeNode> getChildren() { + return children; + } + + } + + private final Map<Long, TreeNode> groupMap = new HashMap<>(); + + public GroupTree(Collection<Group> groups, Collection<Device> devices) { + + for (Group group : groups) { + groupMap.put(group.getId(), new TreeNode(group)); + } + + for (TreeNode node : groupMap.values()) { + if (node.getGroup().getGroupId() != 0) { + node.setParent(groupMap.get(node.getGroup().getGroupId())); + } + } + + Map<Long, TreeNode> deviceMap = new HashMap<>(); + + for (Device device : devices) { + deviceMap.put(device.getId(), new TreeNode(device)); + } + + for (TreeNode node : deviceMap.values()) { + if (node.getDevice().getGroupId() != 0) { + node.setParent(groupMap.get(node.getDevice().getGroupId())); + } + } + + } + + public Collection<Group> getGroups(long groupId) { + Set<TreeNode> results = new HashSet<>(); + getNodes(results, groupMap.get(groupId)); + Collection<Group> groups = new ArrayList<>(); + for (TreeNode node : results) { + if (node.getGroup() != null) { + groups.add(node.getGroup()); + } + } + return groups; + } + + public Collection<Device> getDevices(long groupId) { + Set<TreeNode> results = new HashSet<>(); + getNodes(results, groupMap.get(groupId)); + Collection<Device> devices = new ArrayList<>(); + for (TreeNode node : results) { + if (node.getDevice() != null) { + devices.add(node.getDevice()); + } + } + return devices; + } + + private void getNodes(Set<TreeNode> results, TreeNode node) { + for (TreeNode child : node.getChildren()) { + results.add(child); + getNodes(results, child); + } + } + +} diff --git a/src/org/traccar/database/PermissionsManager.java b/src/org/traccar/database/PermissionsManager.java index c676dea23..dc37bbc84 100644 --- a/src/org/traccar/database/PermissionsManager.java +++ b/src/org/traccar/database/PermissionsManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +16,15 @@ package org.traccar.database; import java.sql.SQLException; -import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.traccar.helper.Log; -import org.traccar.model.Permission; +import org.traccar.model.Device; +import org.traccar.model.DevicePermission; +import org.traccar.model.Group; +import org.traccar.model.GroupPermission; import org.traccar.model.Server; import org.traccar.model.User; @@ -34,13 +36,21 @@ public class PermissionsManager { private final Map<Long, User> users = new HashMap<>(); - private final Map<Long, Set<Long>> permissions = new HashMap<>(); + private final Map<Long, Set<Long>> groupPermissions = new HashMap<>(); + private final Map<Long, Set<Long>> devicePermissions = new HashMap<>(); - private Set<Long> getPermissions(long userId) { - if (!permissions.containsKey(userId)) { - permissions.put(userId, new HashSet<Long>()); + public Set<Long> getGroupPermissions(long userId) { + if (!groupPermissions.containsKey(userId)) { + groupPermissions.put(userId, new HashSet<Long>()); } - return permissions.get(userId); + return groupPermissions.get(userId); + } + + public Set<Long> getDevicePermissions(long userId) { + if (!devicePermissions.containsKey(userId)) { + devicePermissions.put(userId, new HashSet<Long>()); + } + return devicePermissions.get(userId); } public PermissionsManager(DataManager dataManager) { @@ -50,15 +60,31 @@ public class PermissionsManager { public final void refresh() { users.clear(); - permissions.clear(); + groupPermissions.clear(); + devicePermissions.clear(); try { server = dataManager.getServer(); for (User user : dataManager.getUsers()) { users.put(user.getId(), user); } - for (Permission permission : dataManager.getPermissions()) { - getPermissions(permission.getUserId()).add(permission.getDeviceId()); + + GroupTree groupTree = new GroupTree(dataManager.getAllGroups(), dataManager.getAllDevices()); + for (GroupPermission permission : dataManager.getGroupPermissions()) { + Set<Long> userGroupPermissions = getGroupPermissions(permission.getUserId()); + Set<Long> userDevicePermissions = getDevicePermissions(permission.getUserId()); + userGroupPermissions.add(permission.getGroupId()); + for (Group group : groupTree.getGroups(permission.getGroupId())) { + userGroupPermissions.add(group.getId()); + } + for (Device device : groupTree.getDevices(permission.getGroupId())) { + userDevicePermissions.add(device.getId()); + } + } + + for (DevicePermission permission : dataManager.getDevicePermissions()) { + getDevicePermissions(permission.getUserId()).add(permission.getDeviceId()); } + } catch (SQLException error) { Log.warning(error); } @@ -80,12 +106,14 @@ public class PermissionsManager { } } - public Collection<Long> allowedDevices(long userId) { - return getPermissions(userId); + public void checkGroup(long userId, long groupId) throws SecurityException { + if (!getGroupPermissions(userId).contains(groupId)) { + throw new SecurityException("Group access denied"); + } } public void checkDevice(long userId, long deviceId) throws SecurityException { - if (!getPermissions(userId).contains(deviceId)) { + if (!getDevicePermissions(userId).contains(deviceId)) { throw new SecurityException("Device access denied"); } } diff --git a/src/org/traccar/helper/Checksum.java b/src/org/traccar/helper/Checksum.java index e6b6cd2c8..c156e53f7 100644 --- a/src/org/traccar/helper/Checksum.java +++ b/src/org/traccar/helper/Checksum.java @@ -158,13 +158,20 @@ public final class Checksum { public static String nmea(String msg) { int checksum = 0; - byte[] bytes = msg.getBytes(Charset.defaultCharset()); - for (int i = 1; i < msg.length(); i++) { - checksum ^= bytes[i]; + for (byte b : msg.getBytes(Charset.defaultCharset())) { + checksum ^= b; } return String.format("*%02x", checksum).toUpperCase(); } + public static String sum(String msg) { + byte checksum = 0; + for (byte b : msg.getBytes(Charset.defaultCharset())) { + checksum += b; + } + return String.format("%02X", checksum).toUpperCase(); + } + public static long luhn(long imei) { long checksum = 0; long remain = imei; diff --git a/src/org/traccar/helper/ObdDecoder.java b/src/org/traccar/helper/ObdDecoder.java index 35fa4dc07..c2d9b1841 100644 --- a/src/org/traccar/helper/ObdDecoder.java +++ b/src/org/traccar/helper/ObdDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,8 +25,9 @@ public final class ObdDecoder { private ObdDecoder() { } - private static final int MODE_CURRENT = 0x01; - private static final int MODE_FREEZE_FRAME = 0x02; + public static final int MODE_CURRENT = 0x01; + public static final int MODE_FREEZE_FRAME = 0x02; + public static final int MODE_CODES = 0x03; private static final int PID_ENGINE_LOAD = 0x04; private static final int PID_COOLANT_TEMPERATURE = 0x05; @@ -37,11 +38,13 @@ public final class ObdDecoder { private static final int PID_FUEL_LEVEL = 0x2F; private static final int PID_DISTANCE_CLEARED = 0x31; - public static Map.Entry<String, Object> decode(int mode, int pid, String value) { + public static Map.Entry<String, Object> decode(int mode, String value) { switch (mode) { case MODE_CURRENT: case MODE_FREEZE_FRAME: - return decodeData(pid, value); + return decodeData(Integer.parseInt(value.substring(0, 2), 16), value.substring(2)); + case MODE_CODES: + return decodeCodes(value); default: return null; } @@ -51,6 +54,30 @@ public final class ObdDecoder { return new AbstractMap.SimpleEntry<>(key, value); } + private static Map.Entry<String, Object> decodeCodes(String value) { + StringBuilder codes = new StringBuilder(); + for (int i = 0; i < value.length() / 4; i++) { + int numValue = Integer.parseInt(value.substring(i * 4, (i + 1) * 4), 16); + codes.append(','); + switch (numValue >> 14) { + case 1: + codes.append('C'); + break; + case 2: + codes.append('B'); + break; + case 3: + codes.append('U'); + break; + default: + codes.append('P'); + break; + } + codes.append(String.format("%04X", numValue & 0x3FFF)); + } + return createEntry("dtcs", codes.toString().replaceFirst(",", "")); + } + private static Map.Entry<String, Object> decodeData(int pid, String value) { int intValue = Integer.parseInt(value, 16); switch (pid) { @@ -69,7 +96,7 @@ public final class ObdDecoder { case PID_FUEL_LEVEL: return createEntry(Event.KEY_FUEL, intValue * 100 / 255); case PID_DISTANCE_CLEARED: - return createEntry(Event.KEY_FUEL, intValue); + return createEntry("cleared-distance", intValue); default: return null; } diff --git a/src/org/traccar/helper/Parser.java b/src/org/traccar/helper/Parser.java index a3f118df0..dcea1c8f7 100644 --- a/src/org/traccar/helper/Parser.java +++ b/src/org/traccar/helper/Parser.java @@ -37,12 +37,17 @@ public class Parser { return matcher.find(); } + public void skip(int number) { + position += number; + } + public boolean hasNext() { return hasNext(1); } public boolean hasNext(int number) { - if (matcher.group(position) != null) { + String value = matcher.group(position); + if (value != null && !value.isEmpty()) { return true; } else { position += number; diff --git a/src/org/traccar/model/Device.java b/src/org/traccar/model/Device.java index 7ca9fc843..934e753b1 100644 --- a/src/org/traccar/model/Device.java +++ b/src/org/traccar/model/Device.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2015 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2012 - 2016 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -91,4 +91,14 @@ public class Device { this.positionId = positionId; } + private long groupId; + + public long getGroupId() { + return groupId; + } + + public void setGroupId(long groupId) { + this.groupId = groupId; + } + } diff --git a/src/org/traccar/model/Permission.java b/src/org/traccar/model/DevicePermission.java index 393de2359..b3bc0cae0 100644 --- a/src/org/traccar/model/Permission.java +++ b/src/org/traccar/model/DevicePermission.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ */ package org.traccar.model; -public class Permission { +public class DevicePermission { private long userId; diff --git a/src/org/traccar/model/Event.java b/src/org/traccar/model/Event.java index a9f6f9204..221536530 100644 --- a/src/org/traccar/model/Event.java +++ b/src/org/traccar/model/Event.java @@ -29,6 +29,7 @@ public abstract class Event extends Extensible { public static final String KEY_ALARM = "alarm"; public static final String KEY_STATUS = "status"; public static final String KEY_ODOMETER = "odometer"; + public static final String KEY_HOURS = "hours"; public static final String KEY_INPUT = "input"; public static final String KEY_OUTPUT = "output"; public static final String KEY_POWER = "power"; diff --git a/src/org/traccar/model/Group.java b/src/org/traccar/model/Group.java new file mode 100644 index 000000000..00f2b2cfc --- /dev/null +++ b/src/org/traccar/model/Group.java @@ -0,0 +1,50 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.model; + +public class Group { + + private long id; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + private long groupId; + + public long getGroupId() { + return groupId; + } + + public void setGroupId(long groupId) { + this.groupId = groupId; + } + +} diff --git a/src/org/traccar/model/GroupPermission.java b/src/org/traccar/model/GroupPermission.java new file mode 100644 index 000000000..9b0011575 --- /dev/null +++ b/src/org/traccar/model/GroupPermission.java @@ -0,0 +1,40 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.model; + +public class GroupPermission { + + private long userId; + + public long getUserId() { + return userId; + } + + public void setUserId(long userId) { + this.userId = userId; + } + + private long groupId; + + public long getGroupId() { + return groupId; + } + + public void setGroupId(long groupId) { + this.groupId = groupId; + } + +} diff --git a/src/org/traccar/protocol/AplicomProtocolDecoder.java b/src/org/traccar/protocol/AplicomProtocolDecoder.java index 6ae6aa09f..a3d34cf3c 100644 --- a/src/org/traccar/protocol/AplicomProtocolDecoder.java +++ b/src/org/traccar/protocol/AplicomProtocolDecoder.java @@ -288,7 +288,7 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder { } if ((selector & 0x0200) != 0) { - buf.skipBytes(6); // button + position.set(Event.KEY_RFID, (((long) buf.readUnsignedShort()) << 32) + buf.readUnsignedInt()); } if ((selector & 0x0400) != 0) { diff --git a/src/org/traccar/protocol/AstraProtocol.java b/src/org/traccar/protocol/AstraProtocol.java new file mode 100644 index 000000000..d0b1d47a5 --- /dev/null +++ b/src/org/traccar/protocol/AstraProtocol.java @@ -0,0 +1,43 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import org.jboss.netty.bootstrap.ServerBootstrap; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder; +import org.traccar.BaseProtocol; +import org.traccar.TrackerServer; + +import java.util.List; + +public class AstraProtocol extends BaseProtocol { + + public AstraProtocol() { + super("astra"); + } + + @Override + public void initTrackerServers(List<TrackerServer> serverList) { + serverList.add(new TrackerServer(new ServerBootstrap(), this.getName()) { + @Override + protected void addSpecificHandlers(ChannelPipeline pipeline) { + pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 1, 2, -3, 0)); + pipeline.addLast("objectDecoder", new AstraProtocolDecoder(AstraProtocol.this)); + } + }); + } + +} diff --git a/src/org/traccar/protocol/AstraProtocolDecoder.java b/src/org/traccar/protocol/AstraProtocolDecoder.java new file mode 100644 index 000000000..8cfec95fe --- /dev/null +++ b/src/org/traccar/protocol/AstraProtocolDecoder.java @@ -0,0 +1,119 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.jboss.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.BitUtil; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.UnitsConverter; +import org.traccar.model.Event; +import org.traccar.model.Position; + +import java.net.SocketAddress; +import java.nio.charset.Charset; +import java.util.LinkedList; +import java.util.List; + +public class AstraProtocolDecoder extends BaseProtocolDecoder { + + public AstraProtocolDecoder(AstraProtocol protocol) { + super(protocol); + } + + public static final int MSG_HEARTBEAT = 0x1A; + public static final int MSG_DATA = 0x10; + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + ChannelBuffer buf = (ChannelBuffer) msg; + + if (channel != null) { + channel.write(ChannelBuffers.wrappedBuffer(new byte[] {0x06}), remoteAddress); + } + + buf.readUnsignedByte(); // protocol + buf.readUnsignedShort(); // length + + String imei = String.format("%08d", buf.readUnsignedInt()) + String.format("%07d", buf.readUnsignedMedium()); + if (!identify(imei, channel, remoteAddress)) { + return null; + } + + List<Position> positions = new LinkedList<>(); + + while (buf.readableBytes() > 2) { + + Position position = new Position(); + position.setProtocol(getProtocolName()); + position.setDeviceId(getDeviceId()); + + buf.readUnsignedByte(); // index + + position.setLatitude(buf.readInt() * 0.000001); + position.setLongitude(buf.readInt() * 0.000001); + + DateBuilder dateBuilder = new DateBuilder() + .setDate(1980, 1, 6).addMillis(buf.readUnsignedInt() * 1000L); + position.setTime(dateBuilder.getDate()); + + position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte() * 2)); + position.setCourse(buf.readUnsignedByte() * 2); + + int reason = buf.readUnsignedMedium(); + buf.readUnsignedShort(); // status + + position.set(Event.PREFIX_IO + 1, buf.readUnsignedByte()); + position.set(Event.PREFIX_ADC + 1, buf.readUnsignedByte()); + position.set(Event.KEY_BATTERY, buf.readUnsignedByte()); + position.set(Event.KEY_POWER, buf.readUnsignedByte()); + + buf.readUnsignedByte(); // max journey speed + buf.skipBytes(6); // accelerometer + buf.readUnsignedShort(); // journey distance + buf.readUnsignedShort(); // journey idle time + + position.setAltitude(buf.readUnsignedByte() * 20); + + int quality = buf.readUnsignedByte(); + position.set(Event.KEY_SATELLITES, quality & 0xf); + position.set(Event.KEY_GSM, quality >> 4); + + buf.readUnsignedByte(); // geofence events + + if (BitUtil.check(reason, 6) || BitUtil.check(reason, 7)) { + + position.set(Event.KEY_RFID, buf.readBytes(7).toString(Charset.defaultCharset())); + position.set(Event.KEY_ODOMETER, buf.readUnsignedMedium()); + + buf.readUnsignedShort(); // engine time + + } + + // extra data + + positions.add(position); + + } + + return positions; + } + +} diff --git a/src/org/traccar/protocol/BoxProtocol.java b/src/org/traccar/protocol/BoxProtocol.java index ae8207002..33cfca282 100644 --- a/src/org/traccar/protocol/BoxProtocol.java +++ b/src/org/traccar/protocol/BoxProtocol.java @@ -37,8 +37,8 @@ public class BoxProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '\r')); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new BoxProtocolDecoder(BoxProtocol.this)); } }); diff --git a/src/org/traccar/protocol/CarscopProtocol.java b/src/org/traccar/protocol/CarscopProtocol.java index 1ac3e5e9f..4432fb87a 100644 --- a/src/org/traccar/protocol/CarscopProtocol.java +++ b/src/org/traccar/protocol/CarscopProtocol.java @@ -37,8 +37,8 @@ public class CarscopProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '^')); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new CarscopProtocolDecoder(CarscopProtocol.this)); } }); diff --git a/src/org/traccar/protocol/CastelProtocolDecoder.java b/src/org/traccar/protocol/CastelProtocolDecoder.java index 3179cbd78..9f332dd04 100644 --- a/src/org/traccar/protocol/CastelProtocolDecoder.java +++ b/src/org/traccar/protocol/CastelProtocolDecoder.java @@ -109,6 +109,28 @@ public class CastelProtocolDecoder extends BaseProtocolDecoder { } } + private void sendResponse( + Channel channel, SocketAddress remoteAddress, ChannelBuffer id, short type) { + + if (channel != null) { + int length = 2 + 2 + id.readableBytes() + 2 + 4 + 8 + 2 + 2; + + ChannelBuffer response = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, length); + response.writeByte('@'); response.writeByte('@'); + response.writeShort(length); + response.writeBytes(id); + response.writeShort(ChannelBuffers.swapShort(type)); + response.writeInt(0); + for (int i = 0; i < 8; i++) { + response.writeByte(0xff); + } + response.writeShort( + Checksum.crc16(Checksum.CRC16_X25, response.toByteBuffer(0, response.writerIndex()))); + response.writeByte(0x0D); response.writeByte(0x0A); + channel.write(response, remoteAddress); + } + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { @@ -134,23 +156,7 @@ public class CastelProtocolDecoder extends BaseProtocolDecoder { if (type == 0x2001) { - if (channel != null) { - int length = 2 + 2 + id.readableBytes() + 2 + 4 + 8 + 2 + 2; - - ChannelBuffer response = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, length); - response.writeByte('@'); response.writeByte('@'); - response.writeShort(length); - response.writeBytes(id); - response.writeShort(ChannelBuffers.swapShort((short) 0x1001)); - response.writeInt(0); - for (int i = 0; i < 8; i++) { - response.writeByte(0xff); - } - response.writeShort( - Checksum.crc16(Checksum.CRC16_X25, response.toByteBuffer(0, response.writerIndex()))); - response.writeByte(0x0D); response.writeByte(0x0A); - channel.write(response, remoteAddress); - } + sendResponse(channel, remoteAddress, id, (short) 0x1001); buf.readUnsignedInt(); // index buf.readUnsignedInt(); // unix time diff --git a/src/org/traccar/protocol/CityeasyProtocol.java b/src/org/traccar/protocol/CityeasyProtocol.java index 1049e48eb..82302d2d7 100644 --- a/src/org/traccar/protocol/CityeasyProtocol.java +++ b/src/org/traccar/protocol/CityeasyProtocol.java @@ -41,8 +41,8 @@ public class CityeasyProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 2, 2, -4, 0)); - pipeline.addLast("objectDecoder", new CityeasyProtocolDecoder(CityeasyProtocol.this)); pipeline.addLast("objectEncoder", new CityeasyProtocolEncoder()); + pipeline.addLast("objectDecoder", new CityeasyProtocolDecoder(CityeasyProtocol.this)); } }); } diff --git a/src/org/traccar/protocol/FlextrackProtocol.java b/src/org/traccar/protocol/FlextrackProtocol.java index 0d1b13d8a..3b1aaa3d8 100644 --- a/src/org/traccar/protocol/FlextrackProtocol.java +++ b/src/org/traccar/protocol/FlextrackProtocol.java @@ -36,8 +36,8 @@ public class FlextrackProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, "\r")); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new FlextrackProtocolDecoder(FlextrackProtocol.this)); } }); diff --git a/src/org/traccar/protocol/Gl100Protocol.java b/src/org/traccar/protocol/Gl100Protocol.java index cc2c826ab..f51834ea2 100644 --- a/src/org/traccar/protocol/Gl100Protocol.java +++ b/src/org/traccar/protocol/Gl100Protocol.java @@ -38,16 +38,16 @@ public class Gl100Protocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '\0')); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new Gl100ProtocolDecoder(Gl100Protocol.this)); } }); serverList.add(new TrackerServer(new ConnectionlessBootstrap(), this.getName()) { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new Gl100ProtocolDecoder(Gl100Protocol.this)); } }); diff --git a/src/org/traccar/protocol/Gl200Protocol.java b/src/org/traccar/protocol/Gl200Protocol.java index 3aa69567d..701652e12 100644 --- a/src/org/traccar/protocol/Gl200Protocol.java +++ b/src/org/traccar/protocol/Gl200Protocol.java @@ -44,19 +44,19 @@ public class Gl200Protocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, "$", "\0")); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); - pipeline.addLast("objectDecoder", new Gl200ProtocolDecoder(Gl200Protocol.this)); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectEncoder", new Gl200ProtocolEncoder()); + pipeline.addLast("objectDecoder", new Gl200ProtocolDecoder(Gl200Protocol.this)); } }); serverList.add(new TrackerServer(new ConnectionlessBootstrap(), this.getName()) { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); - pipeline.addLast("objectDecoder", new Gl200ProtocolDecoder(Gl200Protocol.this)); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectEncoder", new Gl200ProtocolEncoder()); + pipeline.addLast("objectDecoder", new Gl200ProtocolDecoder(Gl200Protocol.this)); } }); } diff --git a/src/org/traccar/protocol/Gl200ProtocolDecoder.java b/src/org/traccar/protocol/Gl200ProtocolDecoder.java index e4bf43979..574f9a8c3 100644 --- a/src/org/traccar/protocol/Gl200ProtocolDecoder.java +++ b/src/org/traccar/protocol/Gl200ProtocolDecoder.java @@ -16,10 +16,13 @@ package org.traccar.protocol; import java.net.SocketAddress; +import java.util.LinkedList; import java.util.regex.Pattern; + import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; import org.traccar.Context; +import org.traccar.helper.BitUtil; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; @@ -33,7 +36,7 @@ public class Gl200ProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN_HEARTBEAT = new PatternBuilder() + private static final Pattern PATTERN_HBD = new PatternBuilder() .text("+ACK:GTHBD,") .number("([0-9A-Z]{2}xxxx),") .any().text(",") @@ -68,115 +71,204 @@ public class Gl200ProtocolDecoder extends BaseProtocolDecoder { .text("$").optional() .compile(); - private static final Pattern PATTERN = new PatternBuilder() + private static final Pattern PATTERN_LOCATION = new PatternBuilder() + .number("(?:d{1,2})?,") // gps accuracy + .number("(d{1,3}.d)?,") // speed + .number("(d{1,3})?,") // course + .number("(-?d{1,5}.d)?,") // altitude + .number("(-?d{1,3}.d{6})?,") // longitude + .number("(-?d{1,2}.d{6})?,") // latitude + .number("(dddd)(dd)(dd)") // date + .number("(dd)(dd)(dd)").optional(2) // time + .text(",") .groupBegin() - .text("+").expression("(?:RESP|BUFF)").text(":") + .number("(0ddd)?,") // mcc + .number("(0ddd)?,") // mnc + .number("(xxxx)?,") // lac + .number("(xxxx)?,") // cell .or() - .binary("00?04,") - .number("xxxx,") - .expression("[01],") + .number("(d+)?,") // mcc + .number("(d+)?,") // mnc + .number("(d+)?,") // lac + .number("(d+)?,") // cell .groupEnd() - .expression("GT...,") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .expression("([^,]+),") // imei + .number("(?:d+|(d+.d))?,") // odometer + .compile(); - .groupBegin() - .expression("[0-9A-Z]{17},") // vin + private static final Pattern PATTERN_OBD = new PatternBuilder() + .text("+RESP:GTOBD,") + .number("[0-9A-Z]{2}xxxx,") // protocol version + .number("(d{15}),") // imei + .expression("(?:[0-9A-Z]{17})?,") // vin .expression("[^,]{0,20},") // device name .expression("[01],") // report type .number("x{1,8},") // report mask - .expression("[0-9A-Z]{17},") // vin + .expression("(?:[0-9A-Z]{17})?,") // vin .number("[01],") // obd connect - .number("d{1,5},") // obd voltage - .number("x{8},") // support pids - .number("(d{1,5}),") // engine rpm - .number("(d{1,3}),") // speed - .number("(-?d{1,3}),") // coolant temp + .number("(?:d{1,5})?,") // obd voltage + .number("(?:x{8})?,") // support pids + .number("(d{1,5})?,") // engine rpm + .number("(d{1,3})?,") // speed + .number("(-?d{1,3})?,") // coolant temp .number("(d+.?d*|Inf|NaN)?,") // fuel consumption - .number("(d{1,5}),") // dtcs cleared distance - .number("d{1,5},") - .expression("([01]),") // obd connect - .number("(d{1,3}),") // number of dtcs + .number("(d{1,5})?,") // dtcs cleared distance + .number("(?:d{1,5})?,") + .expression("([01])?,") // obd connect + .number("(d{1,3})?,") // number of dtcs .number("(x*),") // dtcs - .number("(d{1,3}),") // throttle - .number("d{1,3},") // engine load + .number("(d{1,3})?,") // throttle + .number("(?:d{1,3})?,") // engine load .number("(d{1,3})?,") // fuel level - .number("(d+)") // odometer - .or().any() - .groupEnd().text(",") - - .number("(d{1,2})?,") // gps accuracy - .number("(d{1,3}.d)?,") // speed - .number("(d{1,3})?,") // course - .number("(-?d{1,5}.d)?,") // altitude - .number("(-?d{1,3}.d{6})?,") // longitude - .number("(-?d{1,2}.d{6})?,") // latitude + .number("(d+),") // odometer + .expression(PATTERN_LOCATION.pattern()) + .number("(d{1,7}.d)?,") // odometer .number("(dddd)(dd)(dd)") // date .number("(dd)(dd)(dd)").optional(2) // time .text(",") - .number("(0ddd)?,") // mcc - .number("(0ddd)?,") // mnc - .number("(?:xxxx)?") - .number("(xxxx)").optional(2) // lac - .text(",") - .number("(xxxx)?,") // cell + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + + private static final Pattern PATTERN_FRI = new PatternBuilder() + .text("+").expression("(?:RESP|BUFF):GTFRI,") + .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .number("(d+)?,") // power + .number("d{1,2},") // report type + .number("d{1,2},") // count + .expression("((?:") + .expression(PATTERN_LOCATION.pattern()) + .expression(")+)") .groupBegin() - .number("(d+.d)?,") // odometer + .number("(d{1,7}.d)?,").optional() // odometer .number("(d{1,3})?,") // battery - .groupEnd("?") + .or() + .number("(d{1,7}.d)?,") // odometer + .number("(d{5}:dd:dd)?,") // hour meter + .number("(x+)?,") // adc 1 + .number("(x+)?,") // adc 2 + .number("(d{1,3})?,") // battery + .number("(?:(xx)(xx)(xx))?,,,,") // device status + .groupEnd() + .number("(dddd)(dd)(dd)") // date + .number("(dd)(dd)(dd)").optional(2) // time + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + + private static final Pattern PATTERN = new PatternBuilder() + .text("+").expression("(?:RESP|BUFF):GT...,") + .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version + .number("(d{15}),") // imei + .expression("[^,]*,") // device name + .number("d*,") + .number("d{1,2},") // report type + .number("d{1,2},") // count + .expression(PATTERN_LOCATION.pattern()) .groupBegin() + .number("(d{1,7}.d)?,").optional() // odometer + .number("(d{1,3})?,") // battery + .or() + .number("(d{1,7}.d)?,") // odometer + .groupEnd() + .number("(dddd)(dd)(dd)") // date + .number("(dd)(dd)(dd)").optional(2) // time + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + + private static final Pattern PATTERN_BASIC = new PatternBuilder() + .text("+").expression("(?:RESP|BUFF)").text(":") + .expression("GT...,") + .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version + .number("(d{15}|x{14}),") // imei .any() + .number("(d{1,3}.d)?,") // speed + .number("(d{1,3})?,") // course + .number("(-?d{1,5}.d)?,") // altitude + .number("(-?d{1,3}.d{6}),") // longitude + .number("(-?d{1,2}.d{6}),") // latitude .number("(dddd)(dd)(dd)") // date .number("(dd)(dd)(dd)") // time - .or() + .text(",") .any() - .groupEnd() - .number(",(xxxx)") + .number("(xxxx)") // count number .text("$").optional() .compile(); - @Override - protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + private Object decodeHbd(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN_HBD, sentence); + if (parser.matches() && channel != null) { + channel.write("+SACK:GTHBD," + parser.next() + "," + parser.next() + "$", remoteAddress); + } + return null; + } - String sentence = (String) msg; + private Object decodeInf(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN_INF, sentence); + if (!parser.matches()) { + return null; + } - Parser parser = new Parser(PATTERN_HEARTBEAT, sentence); - if (parser.matches()) { - if (channel != null) { - channel.write("+SACK:GTHBD," + parser.next() + "," + parser.next() + "$", remoteAddress); - } + Position position = new Position(); + position.setProtocol(getProtocolName()); + + if (!identify(parser.next(), channel, remoteAddress)) { return null; } + position.setDeviceId(getDeviceId()); - parser = new Parser(PATTERN_INF, sentence); - if (parser.matches()) { + position.set(Event.KEY_STATUS, parser.next()); + position.set(Event.KEY_POWER, parser.next()); + position.set(Event.KEY_BATTERY, parser.next()); + position.set(Event.KEY_CHARGE, parser.next()); - Position position = new Position(); - position.setProtocol(getProtocolName()); + DateBuilder dateBuilder = new DateBuilder() + .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); - if (!identify(parser.next(), channel, remoteAddress)) { - return null; - } - position.setDeviceId(getDeviceId()); + getLastLocation(position, dateBuilder.getDate()); + + position.set(Event.KEY_INDEX, parser.next()); - position.set(Event.KEY_STATUS, parser.next()); - position.set(Event.KEY_POWER, parser.next()); - position.set(Event.KEY_BATTERY, parser.next()); - position.set(Event.KEY_CHARGE, parser.next()); + return position; + } + + private void decodeLocation(Position position, Parser parser) { + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble())); + position.setCourse(parser.nextDouble()); + position.setAltitude(parser.nextDouble()); + + if (parser.hasNext(8)) { + position.setValid(true); + position.setLongitude(parser.nextDouble()); + position.setLatitude(parser.nextDouble()); DateBuilder dateBuilder = new DateBuilder() .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt()) .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); + } else { + getLastLocation(position, null); + } - getLastLocation(position, dateBuilder.getDate()); + if (parser.hasNext(4)) { + position.set(Event.KEY_MCC, parser.nextInt()); + position.set(Event.KEY_MNC, parser.nextInt()); + position.set(Event.KEY_LAC, parser.nextInt(16)); + position.set(Event.KEY_CID, parser.nextInt(16)); + } - position.set(Event.KEY_INDEX, parser.next()); + parser.skip(4); // alternative networks - return position; - } + position.set(Event.KEY_ODOMETER, parser.next()); + } - parser = new Parser(PATTERN, sentence); + private Object decodeObd(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN_OBD, sentence); if (!parser.matches()) { return null; } @@ -189,12 +281,6 @@ public class Gl200ProtocolDecoder extends BaseProtocolDecoder { } position.setDeviceId(getDeviceId()); - // RFID - if (sentence.startsWith("+RESP:GTIDA")) { - position.set(Event.KEY_RFID, sentence.split(",")[5]); - } - - // OBD position.set(Event.KEY_RPM, parser.next()); position.set(Event.KEY_OBD_SPEED, parser.next()); position.set(Event.PREFIX_TEMP + 1, parser.next()); @@ -207,32 +293,111 @@ public class Gl200ProtocolDecoder extends BaseProtocolDecoder { position.set(Event.KEY_FUEL, parser.next()); position.set(Event.KEY_OBD_ODOMETER, parser.next()); - if (parser.hasNext(12)) { - position.setValid(parser.nextInt() < 20); - position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble())); - position.setCourse(parser.nextDouble()); - position.setAltitude(parser.nextDouble()); - position.setLongitude(parser.nextDouble()); - position.setLatitude(parser.nextDouble()); + decodeLocation(position, parser); + position.set(Event.KEY_ODOMETER, parser.next()); + + if (parser.hasNext(6)) { DateBuilder dateBuilder = new DateBuilder() .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt()) .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); - position.setTime(dateBuilder.getDate()); - } else { - getLastLocation(position, null); + if (!position.getOutdated() && position.getFixTime().after(dateBuilder.getDate())) { + position.setTime(dateBuilder.getDate()); + } } - if (parser.hasNext(4)) { - position.set(Event.KEY_MCC, parser.nextInt()); - position.set(Event.KEY_MNC, parser.nextInt()); - position.set(Event.KEY_LAC, parser.nextInt(16)); - position.set(Event.KEY_CID, parser.nextInt(16)); + return position; + } + + private Object decodeFri(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN_FRI, sentence); + if (!parser.matches()) { + return null; + } + + LinkedList<Position> positions = new LinkedList<>(); + + if (!identify(parser.next(), channel, remoteAddress)) { + return null; + } + + int power = parser.nextInt(); + + Parser itemParser = new Parser(PATTERN_LOCATION, parser.next()); + while (itemParser.find()) { + Position position = new Position(); + position.setProtocol(getProtocolName()); + position.setDeviceId(getDeviceId()); + + decodeLocation(position, itemParser); + + positions.add(position); + } + + Position position = positions.getLast(); + + decodeLocation(position, parser); + + // power value only on some devices + if (power > 10) { + position.set(Event.KEY_POWER, power); } position.set(Event.KEY_ODOMETER, parser.next()); position.set(Event.KEY_BATTERY, parser.next()); + position.set(Event.KEY_ODOMETER, parser.next()); + position.set(Event.KEY_HOURS, parser.next()); + position.set(Event.PREFIX_ADC + 1, parser.next()); + position.set(Event.PREFIX_ADC + 2, parser.next()); + position.set(Event.KEY_BATTERY, parser.next()); + + if (parser.hasNext(3)) { + int ignition = parser.nextInt(16); + if (BitUtil.check(ignition, 4)) { + position.set(Event.KEY_IGNITION, false); + } else if (BitUtil.check(ignition, 5)) { + position.set(Event.KEY_IGNITION, true); + } + position.set(Event.KEY_INPUT, parser.nextInt(16)); + position.set(Event.KEY_OUTPUT, parser.nextInt(16)); + } + + // workaround for wrong location time + if (parser.hasNext(6)) { + DateBuilder dateBuilder = new DateBuilder() + .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + if (!position.getOutdated() && position.getFixTime().after(dateBuilder.getDate())) { + position.setTime(dateBuilder.getDate()); + } + } + + return positions; + } + + private Object decodeOther(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN, sentence); + if (!parser.matches()) { + return null; + } + + Position position = new Position(); + position.setProtocol(getProtocolName()); + + if (!identify(parser.next(), channel, remoteAddress)) { + return null; + } + position.setDeviceId(getDeviceId()); + + decodeLocation(position, parser); + + position.set(Event.KEY_ODOMETER, parser.next()); + position.set(Event.KEY_BATTERY, parser.next()); + + position.set(Event.KEY_ODOMETER, parser.next()); + + // workaround for wrong location time if (parser.hasNext(6)) { DateBuilder dateBuilder = new DateBuilder() .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt()) @@ -249,4 +414,71 @@ public class Gl200ProtocolDecoder extends BaseProtocolDecoder { return position; } + private Object decodeBasic(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN_BASIC, sentence); + if (!parser.matches()) { + return null; + } + + Position position = new Position(); + position.setProtocol(getProtocolName()); + + if (!identify(parser.next(), channel, remoteAddress)) { + return null; + } + position.setDeviceId(getDeviceId()); + + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble())); + position.setCourse(parser.nextDouble()); + position.setAltitude(parser.nextDouble()); + + position.setValid(true); + position.setLongitude(parser.nextDouble()); + position.setLatitude(parser.nextDouble()); + + DateBuilder dateBuilder = new DateBuilder() + .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); + + return position; + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + String sentence = (String) msg; + + int typeIndex = sentence.indexOf(":GT"); + if (typeIndex < 0) { + return null; + } + + Object result; + switch (sentence.substring(typeIndex + 3, typeIndex + 6)) { + case "HBD": + result = decodeHbd(channel, remoteAddress, sentence); + break; + case "INF": + result = decodeInf(channel, remoteAddress, sentence); + break; + case "OBD": + result = decodeObd(channel, remoteAddress, sentence); + break; + case "FRI": + result = decodeFri(channel, remoteAddress, sentence); + break; + default: + result = decodeOther(channel, remoteAddress, sentence); + break; + } + + if (result == null) { + result = decodeBasic(channel, remoteAddress, sentence); + } + + return result; + } + } diff --git a/src/org/traccar/protocol/GlobalSatProtocol.java b/src/org/traccar/protocol/GlobalSatProtocol.java index 10db8e313..592ba152e 100644 --- a/src/org/traccar/protocol/GlobalSatProtocol.java +++ b/src/org/traccar/protocol/GlobalSatProtocol.java @@ -37,8 +37,8 @@ public class GlobalSatProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '!')); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new GlobalSatProtocolDecoder(GlobalSatProtocol.this)); } }); diff --git a/src/org/traccar/protocol/GoSafeProtocol.java b/src/org/traccar/protocol/GoSafeProtocol.java index af2c2858d..bc1ed14de 100644 --- a/src/org/traccar/protocol/GoSafeProtocol.java +++ b/src/org/traccar/protocol/GoSafeProtocol.java @@ -36,8 +36,8 @@ public class GoSafeProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '#')); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new GoSafeProtocolDecoder(GoSafeProtocol.this)); } }); diff --git a/src/org/traccar/protocol/Gps103Protocol.java b/src/org/traccar/protocol/Gps103Protocol.java index 6ddc2dea1..d8ad0a8b7 100644 --- a/src/org/traccar/protocol/Gps103Protocol.java +++ b/src/org/traccar/protocol/Gps103Protocol.java @@ -47,19 +47,19 @@ public class Gps103Protocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, "\r\n", "\n", ";")); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); - pipeline.addLast("objectDecoder", new Gps103ProtocolDecoder(Gps103Protocol.this)); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectEncoder", new Gps103ProtocolEncoder()); + pipeline.addLast("objectDecoder", new Gps103ProtocolDecoder(Gps103Protocol.this)); } }); serverList.add(new TrackerServer(new ConnectionlessBootstrap(), this.getName()) { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); - pipeline.addLast("objectDecoder", new Gps103ProtocolDecoder(Gps103Protocol.this)); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectEncoder", new Gps103ProtocolEncoder()); + pipeline.addLast("objectDecoder", new Gps103ProtocolDecoder(Gps103Protocol.this)); } }); } diff --git a/src/org/traccar/protocol/Gps103ProtocolDecoder.java b/src/org/traccar/protocol/Gps103ProtocolDecoder.java index a45e20ba2..e2aed6309 100644 --- a/src/org/traccar/protocol/Gps103ProtocolDecoder.java +++ b/src/org/traccar/protocol/Gps103ProtocolDecoder.java @@ -118,11 +118,16 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder { } // Send response #2 - if (sentence.length() == 15 && Character.isDigit(sentence.charAt(0))) { + if (Character.isDigit(sentence.charAt(0))) { if (channel != null) { channel.write("ON", remoteAddress); } - return null; + int start = sentence.indexOf("imei:"); + if (start >= 0) { + sentence = sentence.substring(start); + } else { + return null; + } } Position position = new Position(); diff --git a/src/org/traccar/protocol/GpsGateProtocol.java b/src/org/traccar/protocol/GpsGateProtocol.java index 9f07550e4..c654810b1 100644 --- a/src/org/traccar/protocol/GpsGateProtocol.java +++ b/src/org/traccar/protocol/GpsGateProtocol.java @@ -37,8 +37,8 @@ public class GpsGateProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024)); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new GpsGateProtocolDecoder(GpsGateProtocol.this)); } }); diff --git a/src/org/traccar/protocol/GpsGateProtocolDecoder.java b/src/org/traccar/protocol/GpsGateProtocolDecoder.java index 421dc5304..b48fa6266 100644 --- a/src/org/traccar/protocol/GpsGateProtocolDecoder.java +++ b/src/org/traccar/protocol/GpsGateProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2015 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2013 - 2016 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.traccar.protocol; import java.net.SocketAddress; import java.util.regex.Pattern; + import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; import org.traccar.helper.Checksum; @@ -31,7 +32,7 @@ public class GpsGateProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN = new PatternBuilder() + private static final Pattern PATTERN_GPRMC = new PatternBuilder() .text("$GPRMC,") .number("(dd)(dd)(dd).?(d+)?,") // time .expression("([AV]),") // validity @@ -45,6 +46,24 @@ public class GpsGateProtocolDecoder extends BaseProtocolDecoder { .any() .compile(); + private static final Pattern PATTERN_FRCMD = new PatternBuilder() + .text("$FRCMD,") + .number("(d+),") // imei + .expression("[^,]*,") // command + .expression("[^,]*,") + .number("(dd)(dd.d+),") // latitude + .expression("([NS]),") + .number("(ddd)(dd.d+),") // longitude + .expression("([EW]),") + .number("(d+.?d*),") // altitude + .number("(d+.?d*),") // speed + .number("(d+.?d*),") // course + .number("(dd)(dd)(dd),") // date (ddmmyy) + .number("(dd)(dd)(dd).?(d+)?,") // time + .expression("([01]),") // validity + .any() + .compile(); + private void send(Channel channel, String message) { if (channel != null) { channel.write(message + Checksum.nmea(message) + "\r\n"); @@ -85,10 +104,10 @@ public class GpsGateProtocolDecoder extends BaseProtocolDecoder { // Version check send(channel, "$FRVER,1,0,GpsGate Server 1.0"); - } else if (sentence.startsWith("$GPRMC,") && hasDeviceId()) { + } else if (sentence.startsWith("$GPRMC,")) { - Parser parser = new Parser(PATTERN, sentence); - if (!parser.matches()) { + Parser parser = new Parser(PATTERN_GPRMC, sentence); + if (!parser.matches() || !hasDeviceId()) { return null; } @@ -109,6 +128,37 @@ public class GpsGateProtocolDecoder extends BaseProtocolDecoder { position.setTime(dateBuilder.getDate()); return position; + + } else if (sentence.startsWith("$FRCMD,")) { + + Parser parser = new Parser(PATTERN_FRCMD, sentence); + if (!parser.matches()) { + return null; + } + + Position position = new Position(); + position.setProtocol(getProtocolName()); + + if (!identify(parser.next(), channel, remoteAddress)) { + return null; + } + position.setDeviceId(getDeviceId()); + + position.setLatitude(parser.nextCoordinate()); + position.setLongitude(parser.nextCoordinate()); + position.setAltitude(parser.nextDouble()); + position.setSpeed(parser.nextDouble()); + position.setCourse(parser.nextDouble()); + + DateBuilder dateBuilder = new DateBuilder() + .setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); + + position.setValid(parser.next().equals("1")); + + return position; + } return null; diff --git a/src/org/traccar/protocol/GpsmtaProtocol.java b/src/org/traccar/protocol/GpsmtaProtocol.java index fe586644a..d1eaa2fd3 100644 --- a/src/org/traccar/protocol/GpsmtaProtocol.java +++ b/src/org/traccar/protocol/GpsmtaProtocol.java @@ -35,8 +35,8 @@ public class GpsmtaProtocol extends BaseProtocol { serverList.add(new TrackerServer(new ConnectionlessBootstrap(), this.getName()) { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new GpsmtaProtocolDecoder(GpsmtaProtocol.this)); } }); diff --git a/src/org/traccar/protocol/Gt06FrameDecoder.java b/src/org/traccar/protocol/Gt06FrameDecoder.java index ed0d8d548..67f42efb4 100644 --- a/src/org/traccar/protocol/Gt06FrameDecoder.java +++ b/src/org/traccar/protocol/Gt06FrameDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2014 - 2016 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,9 +24,7 @@ public class Gt06FrameDecoder extends FrameDecoder { @Override protected Object decode( - ChannelHandlerContext ctx, - Channel channel, - ChannelBuffer buf) throws Exception { + ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception { // Check minimum length if (buf.readableBytes() < 5) { @@ -37,6 +35,12 @@ public class Gt06FrameDecoder extends FrameDecoder { if (buf.getByte(buf.readerIndex()) == 0x78) { length += 1 + buf.getUnsignedByte(buf.readerIndex() + 2); + + int type = buf.getUnsignedByte(buf.readerIndex() + 3); + if (type == Gt06ProtocolDecoder.MSG_STATUS && length == 13) { + length += 2; // workaround for #1727 + } + } else { length += 2 + buf.getUnsignedShort(buf.readerIndex() + 2); } diff --git a/src/org/traccar/protocol/Gt06Protocol.java b/src/org/traccar/protocol/Gt06Protocol.java index eac9ad71a..4630342a5 100644 --- a/src/org/traccar/protocol/Gt06Protocol.java +++ b/src/org/traccar/protocol/Gt06Protocol.java @@ -37,8 +37,8 @@ public class Gt06Protocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new Gt06FrameDecoder()); - pipeline.addLast("objectDecoder", new Gt06ProtocolDecoder(Gt06Protocol.this)); pipeline.addLast("objectEncoder", new Gt06ProtocolEncoder()); + pipeline.addLast("objectDecoder", new Gt06ProtocolDecoder(Gt06Protocol.this)); } }); } diff --git a/src/org/traccar/protocol/HomtecsProtocol.java b/src/org/traccar/protocol/HomtecsProtocol.java new file mode 100644 index 000000000..1a2e98565 --- /dev/null +++ b/src/org/traccar/protocol/HomtecsProtocol.java @@ -0,0 +1,43 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import org.jboss.netty.bootstrap.ConnectionlessBootstrap; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.handler.codec.string.StringDecoder; +import org.traccar.BaseProtocol; +import org.traccar.TrackerServer; + +import java.util.List; + +public class HomtecsProtocol extends BaseProtocol { + + public HomtecsProtocol() { + super("homtecs"); + } + + @Override + public void initTrackerServers(List<TrackerServer> serverList) { + serverList.add(new TrackerServer(new ConnectionlessBootstrap(), this.getName()) { + @Override + protected void addSpecificHandlers(ChannelPipeline pipeline) { + pipeline.addLast("stringDecoder", new StringDecoder()); + pipeline.addLast("objectDecoder", new HomtecsProtocolDecoder(HomtecsProtocol.this)); + } + }); + } + +} diff --git a/src/org/traccar/protocol/HomtecsProtocolDecoder.java b/src/org/traccar/protocol/HomtecsProtocolDecoder.java new file mode 100644 index 000000000..0b1cd99e9 --- /dev/null +++ b/src/org/traccar/protocol/HomtecsProtocolDecoder.java @@ -0,0 +1,83 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import org.jboss.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; +import org.traccar.model.Event; +import org.traccar.model.Position; + +import java.net.SocketAddress; +import java.util.regex.Pattern; + +public class HomtecsProtocolDecoder extends BaseProtocolDecoder { + + public HomtecsProtocolDecoder(HomtecsProtocol protocol) { + super(protocol); + } + + private static final Pattern PATTERN = new PatternBuilder() + .expression("([^,]+),") // id + .number("(dd)(dd)(dd),") // date + .number("(dd)(dd)(dd).(d+),") // time + .number("(d+),") // satellites + .number("(dd)(dd.d+),") // latitude + .expression("([NS]),") + .number("(ddd)(dd.d+),") // longitude + .expression("([EW]),") + .number("(d+.?d*)?,") // speed + .number("(d+.?d*)?,") // course + .any() + .compile(); + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + Parser parser = new Parser(PATTERN, (String) msg); + if (!parser.matches()) { + return null; + } + + Position position = new Position(); + position.setProtocol(getProtocolName()); + + if (!identify(parser.next(), channel, remoteAddress)) { + return null; + } + position.setDeviceId(getDeviceId()); + + DateBuilder dateBuilder = new DateBuilder() + .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextInt()); + + position.setTime(dateBuilder.getDate()); + + position.setValid(true); + position.set(Event.KEY_SATELLITES, parser.nextInt()); + + position.setLatitude(parser.nextCoordinate()); + position.setLongitude(parser.nextCoordinate()); + position.setSpeed(parser.nextDouble()); + position.setCourse(parser.nextDouble()); + + return position; + } + +} diff --git a/src/org/traccar/protocol/IntellitracProtocol.java b/src/org/traccar/protocol/IntellitracProtocol.java index c06585547..a6caf770f 100644 --- a/src/org/traccar/protocol/IntellitracProtocol.java +++ b/src/org/traccar/protocol/IntellitracProtocol.java @@ -36,8 +36,8 @@ public class IntellitracProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new IntellitracFrameDecoder(1024)); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new IntellitracProtocolDecoder(IntellitracProtocol.this)); } }); diff --git a/src/org/traccar/protocol/KenjiProtocolDecoder.java b/src/org/traccar/protocol/KenjiProtocolDecoder.java index e1f8130fa..e190af948 100755 --- a/src/org/traccar/protocol/KenjiProtocolDecoder.java +++ b/src/org/traccar/protocol/KenjiProtocolDecoder.java @@ -47,7 +47,7 @@ public class KenjiProtocolDecoder extends BaseProtocolDecoder { .number("G(d+)") // satellites .any() .compile(); - + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { diff --git a/src/org/traccar/protocol/KhdProtocol.java b/src/org/traccar/protocol/KhdProtocol.java index a2edbfd5e..93edb0415 100644 --- a/src/org/traccar/protocol/KhdProtocol.java +++ b/src/org/traccar/protocol/KhdProtocol.java @@ -39,8 +39,8 @@ public class KhdProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(512, 3, 2)); - pipeline.addLast("objectDecoder", new KhdProtocolDecoder(KhdProtocol.this)); pipeline.addLast("objectEncoder", new KhdProtocolEncoder()); + pipeline.addLast("objectDecoder", new KhdProtocolDecoder(KhdProtocol.this)); } }); } diff --git a/src/org/traccar/protocol/LaipacProtocol.java b/src/org/traccar/protocol/LaipacProtocol.java index fff7b1c32..c2e5c585e 100644 --- a/src/org/traccar/protocol/LaipacProtocol.java +++ b/src/org/traccar/protocol/LaipacProtocol.java @@ -37,8 +37,8 @@ public class LaipacProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024)); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new LaipacProtocolDecoder(LaipacProtocol.this)); } }); diff --git a/src/org/traccar/protocol/ManPowerProtocol.java b/src/org/traccar/protocol/ManPowerProtocol.java index 4fc194963..647a1bea7 100644 --- a/src/org/traccar/protocol/ManPowerProtocol.java +++ b/src/org/traccar/protocol/ManPowerProtocol.java @@ -37,8 +37,8 @@ public class ManPowerProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, ';')); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new ManPowerProtocolDecoder(ManPowerProtocol.this)); } }); diff --git a/src/org/traccar/protocol/MegastekProtocol.java b/src/org/traccar/protocol/MegastekProtocol.java index cdf891afe..f61a4443c 100644 --- a/src/org/traccar/protocol/MegastekProtocol.java +++ b/src/org/traccar/protocol/MegastekProtocol.java @@ -36,8 +36,8 @@ public class MegastekProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new MegastekFrameDecoder()); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new MegastekProtocolDecoder(MegastekProtocol.this)); } }); diff --git a/src/org/traccar/protocol/MeiligaoProtocol.java b/src/org/traccar/protocol/MeiligaoProtocol.java index 05dd8834b..2e4226e19 100644 --- a/src/org/traccar/protocol/MeiligaoProtocol.java +++ b/src/org/traccar/protocol/MeiligaoProtocol.java @@ -43,15 +43,15 @@ public class MeiligaoProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new MeiligaoFrameDecoder()); - pipeline.addLast("objectDecoder", new MeiligaoProtocolDecoder(MeiligaoProtocol.this)); pipeline.addLast("objectEncoder", new MeiligaoProtocolEncoder()); + pipeline.addLast("objectDecoder", new MeiligaoProtocolDecoder(MeiligaoProtocol.this)); } }); serverList.add(new TrackerServer(new ConnectionlessBootstrap(), this.getName()) { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { - pipeline.addLast("objectDecoder", new MeiligaoProtocolDecoder(MeiligaoProtocol.this)); pipeline.addLast("objectEncoder", new MeiligaoProtocolEncoder()); + pipeline.addLast("objectDecoder", new MeiligaoProtocolDecoder(MeiligaoProtocol.this)); } }); } diff --git a/src/org/traccar/protocol/MeiligaoProtocolDecoder.java b/src/org/traccar/protocol/MeiligaoProtocolDecoder.java index cb2139a00..5c17aab61 100644 --- a/src/org/traccar/protocol/MeiligaoProtocolDecoder.java +++ b/src/org/traccar/protocol/MeiligaoProtocolDecoder.java @@ -54,23 +54,19 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder { .number("|(xxxx)?") // state .groupBegin() .number("|(xxxx),(xxxx)") // adc + .number("(?:,(xxxx),(xxxx),(xxxx),(xxxx),(xxxx),(xxxx))?") .groupBegin() - .number(",(xxxx),(xxxx),(xxxx),(xxxx),(xxxx),(xxxx)") - .groupEnd("?") - .groupBegin() - .text("|") - .groupBegin() - .number("(x{16})") // cell + .number("|x{16}") // cell .number("|(xx)") // gsm - .number("|(x{8})|") // odometer - .number("(x{9})") // odometer + .number("|(x{8})") // odometer + .or() + .number("|(x{9})") // odometer .groupBegin() .number("|(x{5,})") // rfid .groupEnd("?") .groupEnd("?") .groupEnd("?") .groupEnd("?") - .groupEnd("?") .any() .compile(); @@ -283,15 +279,14 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder { } } - position.set(Event.KEY_CID, parser.next()); - if (parser.hasNext()) { position.set(Event.KEY_GSM, parser.nextInt(16)); } if (parser.hasNext()) { position.set(Event.KEY_ODOMETER, parser.nextInt(16)); - } else if (parser.hasNext()) { + } + if (parser.hasNext()) { position.set(Event.KEY_ODOMETER, parser.nextInt(16)); } diff --git a/src/org/traccar/protocol/MeitrackProtocol.java b/src/org/traccar/protocol/MeitrackProtocol.java index c957d4ea1..e5b5ddf2b 100644 --- a/src/org/traccar/protocol/MeitrackProtocol.java +++ b/src/org/traccar/protocol/MeitrackProtocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,10 +31,12 @@ public class MeitrackProtocol extends BaseProtocol { public MeitrackProtocol() { super("meitrack"); setSupportedCommands( + Command.TYPE_POSITION_SINGLE, Command.TYPE_ENGINE_STOP, Command.TYPE_ENGINE_RESUME, Command.TYPE_ALARM_ARM, - Command.TYPE_ALARM_DISARM); + Command.TYPE_ALARM_DISARM, + Command.TYPE_REQUEST_PHOTO); } @Override @@ -44,6 +46,7 @@ public class MeitrackProtocol extends BaseProtocol { protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new MeitrackFrameDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("objectEncoder", new MeitrackProtocolEncoder()); pipeline.addLast("objectDecoder", new MeitrackProtocolDecoder(MeitrackProtocol.this)); } }; @@ -53,6 +56,7 @@ public class MeitrackProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("objectEncoder", new MeitrackProtocolEncoder()); pipeline.addLast("objectDecoder", new MeitrackProtocolDecoder(MeitrackProtocol.this)); } }; diff --git a/src/org/traccar/protocol/MeitrackProtocolDecoder.java b/src/org/traccar/protocol/MeitrackProtocolDecoder.java index e0ca01412..a920c6e76 100644 --- a/src/org/traccar/protocol/MeitrackProtocolDecoder.java +++ b/src/org/traccar/protocol/MeitrackProtocolDecoder.java @@ -24,6 +24,7 @@ import java.util.regex.Pattern; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.Context; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; @@ -237,10 +238,17 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { index = buf.indexOf(index + 1, buf.writerIndex(), (byte) ','); String type = buf.toString(index + 1, 3, Charset.defaultCharset()); - if (type.equals("CCC")) { - return decodeBinaryMessage(channel, remoteAddress, buf); - } else { - return decodeRegularMessage(channel, remoteAddress, buf); + switch (type) { + case "D03": + if (channel != null) { + String imei = Context.getIdentityManager().getDeviceById(getDeviceId()).getUniqueId(); + channel.write("@@O46," + imei + ",D00,camera_picture.jpg,0*00\r\n"); + } + return null; + case "CCC": + return decodeBinaryMessage(channel, remoteAddress, buf); + default: + return decodeRegularMessage(channel, remoteAddress, buf); } } diff --git a/src/org/traccar/protocol/MeitrackProtocolEncoder.java b/src/org/traccar/protocol/MeitrackProtocolEncoder.java index bfda2b7d2..381935c58 100644 --- a/src/org/traccar/protocol/MeitrackProtocolEncoder.java +++ b/src/org/traccar/protocol/MeitrackProtocolEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,23 +16,36 @@ package org.traccar.protocol; import org.traccar.StringProtocolEncoder; +import org.traccar.helper.Checksum; import org.traccar.helper.Log; import org.traccar.model.Command; public class MeitrackProtocolEncoder extends StringProtocolEncoder { + private Object formatCommand(Command command, char dataId, String content) { + String uniqueId = getUniqueId(command.getDeviceId()); + int length = 1 + uniqueId.length() + 1 + content.length() + 5; + String result = String.format("@@%c%02d,%s,%s*", dataId, length, uniqueId, content); + result += Checksum.sum(result) + "\r\n"; + return result; + } + @Override protected Object encodeCommand(Command command) { switch (command.getType()) { + case Command.TYPE_POSITION_SINGLE: + return formatCommand(command, 'Q', "A10"); case Command.TYPE_ENGINE_STOP: - return formatCommand(command, "@@M33,{%s},C01,0,12222*18\r\n", Command.KEY_UNIQUE_ID); + return formatCommand(command, 'M', "C01,0,12222"); case Command.TYPE_ENGINE_RESUME: - return formatCommand(command, "@@M33,{%s},C01,0,02222*18\r\n", Command.KEY_UNIQUE_ID); + return formatCommand(command, 'M', "C01,0,02222"); case Command.TYPE_ALARM_ARM: - return formatCommand(command, "@@M33,{%s},C01,0,22122*18\r\n", Command.KEY_UNIQUE_ID); + return formatCommand(command, 'M', "C01,0,22122"); case Command.TYPE_ALARM_DISARM: - return formatCommand(command, "@@M33,{%s},C01,0,22022*18\r\n", Command.KEY_UNIQUE_ID); + return formatCommand(command, 'M', "C01,0,22022"); + case Command.TYPE_REQUEST_PHOTO: + return formatCommand(command, 'D', "D03,1,camera_picture.jpg"); default: Log.warning(new UnsupportedOperationException(command.getType())); break; diff --git a/src/org/traccar/protocol/Mta6Protocol.java b/src/org/traccar/protocol/Mta6Protocol.java index 8232b850c..607bf8118 100644 --- a/src/org/traccar/protocol/Mta6Protocol.java +++ b/src/org/traccar/protocol/Mta6Protocol.java @@ -35,8 +35,8 @@ public class Mta6Protocol extends BaseProtocol { serverList.add(new TrackerServer(new ServerBootstrap(), this.getName()) { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { - pipeline.addLast("httpDecoder", new HttpRequestDecoder()); pipeline.addLast("httpEncoder", new HttpResponseEncoder()); + pipeline.addLast("httpDecoder", new HttpRequestDecoder()); pipeline.addLast("objectDecoder", new Mta6ProtocolDecoder( Mta6Protocol.this, !Context.getConfig().getBoolean(getName() + ".can"))); } diff --git a/src/org/traccar/protocol/MtxProtocol.java b/src/org/traccar/protocol/MtxProtocol.java index 4fba981a5..e0bef8350 100644 --- a/src/org/traccar/protocol/MtxProtocol.java +++ b/src/org/traccar/protocol/MtxProtocol.java @@ -37,8 +37,8 @@ public class MtxProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024)); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new MtxProtocolDecoder(MtxProtocol.this)); } }); diff --git a/src/org/traccar/protocol/MxtProtocolDecoder.java b/src/org/traccar/protocol/MxtProtocolDecoder.java index fedb7c715..53d5b8b09 100644 --- a/src/org/traccar/protocol/MxtProtocolDecoder.java +++ b/src/org/traccar/protocol/MxtProtocolDecoder.java @@ -114,7 +114,7 @@ public class MxtProtocolDecoder extends BaseProtocolDecoder { } if (BitUtil.check(infoGroups, 4)) { - position.set("hours", buf.readUnsignedInt()); + position.set(Event.KEY_HOURS, buf.readUnsignedInt()); } if (BitUtil.check(infoGroups, 5)) { diff --git a/src/org/traccar/protocol/OsmAndProtocol.java b/src/org/traccar/protocol/OsmAndProtocol.java index 1e60ae807..3b473c435 100644 --- a/src/org/traccar/protocol/OsmAndProtocol.java +++ b/src/org/traccar/protocol/OsmAndProtocol.java @@ -35,8 +35,8 @@ public class OsmAndProtocol extends BaseProtocol { serverList.add(new TrackerServer(new ServerBootstrap(), this.getName()) { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { - pipeline.addLast("httpDecoder", new HttpRequestDecoder()); pipeline.addLast("httpEncoder", new HttpResponseEncoder()); + pipeline.addLast("httpDecoder", new HttpRequestDecoder()); pipeline.addLast("objectDecoder", new OsmAndProtocolDecoder(OsmAndProtocol.this)); } }); diff --git a/src/org/traccar/protocol/PathAwayProtocol.java b/src/org/traccar/protocol/PathAwayProtocol.java index dfa34abd5..d7efdfaf6 100644 --- a/src/org/traccar/protocol/PathAwayProtocol.java +++ b/src/org/traccar/protocol/PathAwayProtocol.java @@ -35,8 +35,8 @@ public class PathAwayProtocol extends BaseProtocol { serverList.add(new TrackerServer(new ServerBootstrap(), this.getName()) { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { - pipeline.addLast("httpDecoder", new HttpRequestDecoder()); pipeline.addLast("httpEncoder", new HttpResponseEncoder()); + pipeline.addLast("httpDecoder", new HttpRequestDecoder()); pipeline.addLast("objectDecoder", new PathAwayProtocolDecoder(PathAwayProtocol.this)); } }); diff --git a/src/org/traccar/protocol/PiligrimProtocol.java b/src/org/traccar/protocol/PiligrimProtocol.java index 0478835d9..11aafa412 100644 --- a/src/org/traccar/protocol/PiligrimProtocol.java +++ b/src/org/traccar/protocol/PiligrimProtocol.java @@ -36,9 +36,9 @@ public class PiligrimProtocol extends BaseProtocol { serverList.add(new TrackerServer(new ServerBootstrap(), this.getName()) { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { + pipeline.addLast("httpEncoder", new HttpResponseEncoder()); pipeline.addLast("httpDecoder", new HttpRequestDecoder()); pipeline.addLast("httpAggregator", new HttpChunkAggregator(16384)); - pipeline.addLast("httpEncoder", new HttpResponseEncoder()); pipeline.addLast("objectDecoder", new PiligrimProtocolDecoder(PiligrimProtocol.this)); } }); diff --git a/src/org/traccar/protocol/Pt3000Protocol.java b/src/org/traccar/protocol/Pt3000Protocol.java index bde8e7709..3a22be214 100644 --- a/src/org/traccar/protocol/Pt3000Protocol.java +++ b/src/org/traccar/protocol/Pt3000Protocol.java @@ -37,8 +37,8 @@ public class Pt3000Protocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, 'd')); // probably wrong - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new Pt3000ProtocolDecoder(Pt3000Protocol.this)); } }); diff --git a/src/org/traccar/protocol/SuntechProtocol.java b/src/org/traccar/protocol/SuntechProtocol.java index d3f5d6009..e06ed2aac 100644 --- a/src/org/traccar/protocol/SuntechProtocol.java +++ b/src/org/traccar/protocol/SuntechProtocol.java @@ -41,10 +41,10 @@ public class SuntechProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '\r')); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); - pipeline.addLast("objectDecoder", new SuntechProtocolDecoder(SuntechProtocol.this)); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectEncoder", new SuntechProtocolEncoder()); + pipeline.addLast("objectDecoder", new SuntechProtocolDecoder(SuntechProtocol.this)); } }); } diff --git a/src/org/traccar/protocol/T55Protocol.java b/src/org/traccar/protocol/T55Protocol.java index 21f1d3e6c..041413a65 100644 --- a/src/org/traccar/protocol/T55Protocol.java +++ b/src/org/traccar/protocol/T55Protocol.java @@ -37,8 +37,8 @@ public class T55Protocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024)); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new T55ProtocolDecoder(T55Protocol.this)); } }); diff --git a/src/org/traccar/protocol/T55ProtocolDecoder.java b/src/org/traccar/protocol/T55ProtocolDecoder.java index 9c47d1ab0..71f493318 100644 --- a/src/org/traccar/protocol/T55ProtocolDecoder.java +++ b/src/org/traccar/protocol/T55ProtocolDecoder.java @@ -42,7 +42,13 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { .expression("([EW]),") .number("(d+.?d*)?,") // speed .number("(d+.?d*)?,") // course - .number("(dd)(dd)(dd)") // date + .number("(dd)(dd)(dd),") // date + .expression("[^*]+") + .text("*") + .expression("[^,]+") + .number(",(d+)") // satellites + .number(",(d+)") // imei + .number(",(d+)").optional(3) .any() .compile(); @@ -84,7 +90,7 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { private Position position = null; - private Position decodeGprmc(String sentence, Channel channel) { + private Position decodeGprmc(String sentence, SocketAddress remoteAddress, Channel channel) { if (channel != null) { channel.write("OK1\r\n"); @@ -114,6 +120,14 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt()); position.setTime(dateBuilder.getDate()); + if (parser.hasNext(3)) { + position.set(Event.KEY_SATELLITES, parser.next()); + if (!identify(parser.next(), channel, remoteAddress)) { + return null; + } + position.setDeviceId(getDeviceId()); + } + if (hasDeviceId()) { return position; } else { @@ -217,7 +231,7 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { } else if (sentence.startsWith("IMEI")) { identify(sentence.substring(5, sentence.length()), channel, remoteAddress); } else if (sentence.startsWith("$GPFID")) { - if (identify(sentence.substring(6, sentence.length()), channel, remoteAddress) && position != null) { + if (identify(sentence.substring(7, sentence.length()), channel, remoteAddress) && position != null) { Position position = this.position; position.setDeviceId(getDeviceId()); this.position = null; @@ -226,7 +240,7 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { } else if (Character.isDigit(sentence.charAt(0)) && sentence.length() == 15) { identify(sentence, channel, remoteAddress); } else if (sentence.startsWith("$GPRMC")) { - return decodeGprmc(sentence, channel); + return decodeGprmc(sentence, remoteAddress, channel); } else if (sentence.startsWith("$GPGGA") && hasDeviceId()) { return decodeGpgga(sentence); } else if (sentence.startsWith("$GPRMA") && hasDeviceId()) { diff --git a/src/org/traccar/protocol/TaipProtocol.java b/src/org/traccar/protocol/TaipProtocol.java index 439fe7696..a3b744e70 100644 --- a/src/org/traccar/protocol/TaipProtocol.java +++ b/src/org/traccar/protocol/TaipProtocol.java @@ -38,14 +38,15 @@ public class TaipProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '<')); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new TaipProtocolDecoder(TaipProtocol.this, true)); } }); serverList.add(new TrackerServer(new ConnectionlessBootstrap(), this.getName()) { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { + pipeline.addLast("stringEncoder", new StringEncoder()); pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new TaipProtocolDecoder(TaipProtocol.this, false)); } diff --git a/src/org/traccar/protocol/TelicFrameDecoder.java b/src/org/traccar/protocol/TelicFrameDecoder.java new file mode 100644 index 000000000..2a6e121cf --- /dev/null +++ b/src/org/traccar/protocol/TelicFrameDecoder.java @@ -0,0 +1,54 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.handler.codec.frame.FrameDecoder; + +public class TelicFrameDecoder extends FrameDecoder { + + @Override + protected Object decode( + ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception { + + if (buf.readableBytes() < 4) { + return null; + } + + long length = buf.getUnsignedInt(buf.readerIndex()); + + if (length < 1024) { + if (buf.readableBytes() >= length + 4) { + buf.readUnsignedInt(); + return buf.readBytes((int) length); + } + } else { + int endIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 0); + if (endIndex >= 0) { + ChannelBuffer frame = buf.readBytes(endIndex - buf.readerIndex()); + buf.readByte(); + if (frame.readableBytes() > 0) { + return frame; + } + } + } + + return null; + } + +} diff --git a/src/org/traccar/protocol/TelicProtocol.java b/src/org/traccar/protocol/TelicProtocol.java new file mode 100644 index 000000000..464892431 --- /dev/null +++ b/src/org/traccar/protocol/TelicProtocol.java @@ -0,0 +1,47 @@ +/* + * Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import org.jboss.netty.bootstrap.ServerBootstrap; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.handler.codec.string.StringDecoder; +import org.traccar.BaseProtocol; +import org.traccar.TrackerServer; + +import java.nio.ByteOrder; +import java.util.List; + +public class TelicProtocol extends BaseProtocol { + + public TelicProtocol() { + super("telic"); + } + + @Override + public void initTrackerServers(List<TrackerServer> serverList) { + TrackerServer server = new TrackerServer(new ServerBootstrap(), this.getName()) { + @Override + protected void addSpecificHandlers(ChannelPipeline pipeline) { + pipeline.addLast("frameDecoder", new TelicFrameDecoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); + pipeline.addLast("objectDecoder", new TelicProtocolDecoder(TelicProtocol.this)); + } + }; + server.setEndianness(ByteOrder.LITTLE_ENDIAN); + serverList.add(server); + } + +} diff --git a/src/org/traccar/protocol/TelikProtocolDecoder.java b/src/org/traccar/protocol/TelicProtocolDecoder.java index 4171750d6..ba6d9c47e 100644 --- a/src/org/traccar/protocol/TelikProtocolDecoder.java +++ b/src/org/traccar/protocol/TelicProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 - 2015 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2014 - 2016 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,9 +25,9 @@ import org.traccar.helper.PatternBuilder; import org.traccar.model.Event; import org.traccar.model.Position; -public class TelikProtocolDecoder extends BaseProtocolDecoder { +public class TelicProtocolDecoder extends BaseProtocolDecoder { - public TelikProtocolDecoder(TelikProtocol protocol) { + public TelicProtocolDecoder(TelicProtocol protocol) { super(protocol); } @@ -39,12 +39,19 @@ public class TelikProtocolDecoder extends BaseProtocolDecoder { .number("d+,") .number("(dd)(dd)(dd)") // date .number("(dd)(dd)(dd),") // time + .groupBegin() + .number("(ddd)(dd)(dddd),") // longitude + .number("(dd)(dd)(dddd),") // latitude + .or() .number("(-?d+),") // longitude .number("(-?d+),") // latitude + .groupEnd() .number("(d),") // validity .number("(d+),") // speed .number("(d+),") // course .number("(d+),") // satellites + .expression("(?:[^,]*,){7}") + .number("(d+),") .any() .compile(); @@ -72,13 +79,22 @@ public class TelikProtocolDecoder extends BaseProtocolDecoder { .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); position.setTime(dateBuilder.getDate()); - position.setLongitude(parser.nextDouble() / 10000); - position.setLatitude(parser.nextDouble() / 10000); + if (parser.hasNext(6)) { + position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_MIN_MIN)); + position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_MIN_MIN)); + } + + if (parser.hasNext(2)) { + position.setLongitude(parser.nextDouble() / 10000); + position.setLatitude(parser.nextDouble() / 10000); + } + position.setValid(parser.nextInt() != 1); position.setSpeed(parser.nextDouble()); position.setCourse(parser.nextDouble()); position.set(Event.KEY_SATELLITES, parser.next()); + position.set(Event.KEY_BATTERY, parser.nextInt()); return position; } diff --git a/src/org/traccar/protocol/TeltonikaProtocol.java b/src/org/traccar/protocol/TeltonikaProtocol.java index 859b2a08c..f944c3003 100644 --- a/src/org/traccar/protocol/TeltonikaProtocol.java +++ b/src/org/traccar/protocol/TeltonikaProtocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ */ package org.traccar.protocol; +import org.jboss.netty.bootstrap.ConnectionlessBootstrap; import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.channel.ChannelPipeline; import org.traccar.BaseProtocol; @@ -37,6 +38,12 @@ public class TeltonikaProtocol extends BaseProtocol { pipeline.addLast("objectDecoder", new TeltonikaProtocolDecoder(TeltonikaProtocol.this)); } }); + serverList.add(new TrackerServer(new ConnectionlessBootstrap(), this.getName()) { + @Override + protected void addSpecificHandlers(ChannelPipeline pipeline) { + pipeline.addLast("objectDecoder", new TeltonikaProtocolDecoder(TeltonikaProtocol.this)); + } + }); } } diff --git a/src/org/traccar/protocol/Tk102Protocol.java b/src/org/traccar/protocol/Tk102Protocol.java index 4e1d067f9..1f4eda730 100644 --- a/src/org/traccar/protocol/Tk102Protocol.java +++ b/src/org/traccar/protocol/Tk102Protocol.java @@ -37,8 +37,8 @@ public class Tk102Protocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, ']')); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new Tk102ProtocolDecoder(Tk102Protocol.this)); } }); diff --git a/src/org/traccar/protocol/Tk103Protocol.java b/src/org/traccar/protocol/Tk103Protocol.java index b14264091..324c01a31 100644 --- a/src/org/traccar/protocol/Tk103Protocol.java +++ b/src/org/traccar/protocol/Tk103Protocol.java @@ -38,16 +38,16 @@ public class Tk103Protocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, ')')); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new Tk103ProtocolDecoder(Tk103Protocol.this)); } }); serverList.add(new TrackerServer(new ConnectionlessBootstrap(), this.getName()) { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new Tk103ProtocolDecoder(Tk103Protocol.this)); } }); diff --git a/src/org/traccar/protocol/Tlt2hProtocol.java b/src/org/traccar/protocol/Tlt2hProtocol.java index 3a1bf99ff..14787537d 100644 --- a/src/org/traccar/protocol/Tlt2hProtocol.java +++ b/src/org/traccar/protocol/Tlt2hProtocol.java @@ -37,8 +37,8 @@ public class Tlt2hProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(32 * 1024, "##\r\n")); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new Tlt2hProtocolDecoder(Tlt2hProtocol.this)); } }); diff --git a/src/org/traccar/protocol/TotemProtocol.java b/src/org/traccar/protocol/TotemProtocol.java index 097f6a593..4d7f0a135 100644 --- a/src/org/traccar/protocol/TotemProtocol.java +++ b/src/org/traccar/protocol/TotemProtocol.java @@ -41,10 +41,10 @@ public class TotemProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new TotemFrameDecoder()); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); - pipeline.addLast("objectDecoder", new TotemProtocolDecoder(TotemProtocol.this)); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectEncoder", new TotemProtocolEncoder()); + pipeline.addLast("objectDecoder", new TotemProtocolDecoder(TotemProtocol.this)); } }); } diff --git a/src/org/traccar/protocol/Tr20Protocol.java b/src/org/traccar/protocol/Tr20Protocol.java index 8c5a27ae2..4e55d5f98 100644 --- a/src/org/traccar/protocol/Tr20Protocol.java +++ b/src/org/traccar/protocol/Tr20Protocol.java @@ -37,8 +37,8 @@ public class Tr20Protocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024)); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new Tr20ProtocolDecoder(Tr20Protocol.this)); } }); diff --git a/src/org/traccar/protocol/Tr900Protocol.java b/src/org/traccar/protocol/Tr900Protocol.java index 4db0a2239..d54e4cb3c 100644 --- a/src/org/traccar/protocol/Tr900Protocol.java +++ b/src/org/traccar/protocol/Tr900Protocol.java @@ -38,16 +38,16 @@ public class Tr900Protocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024)); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new Tr900ProtocolDecoder(Tr900Protocol.this)); } }); serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new Tr900ProtocolDecoder(Tr900Protocol.this)); } }); diff --git a/src/org/traccar/protocol/TrackboxProtocol.java b/src/org/traccar/protocol/TrackboxProtocol.java index 68554394e..5477de852 100644 --- a/src/org/traccar/protocol/TrackboxProtocol.java +++ b/src/org/traccar/protocol/TrackboxProtocol.java @@ -37,8 +37,8 @@ public class TrackboxProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024)); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new TrackboxProtocolDecoder(TrackboxProtocol.this)); } }); diff --git a/src/org/traccar/protocol/TrvProtocol.java b/src/org/traccar/protocol/TrvProtocol.java index 916b7d612..af598ea02 100644 --- a/src/org/traccar/protocol/TrvProtocol.java +++ b/src/org/traccar/protocol/TrvProtocol.java @@ -37,8 +37,8 @@ public class TrvProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '#')); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new TrvProtocolDecoder(TrvProtocol.this)); } }); diff --git a/src/org/traccar/protocol/TrvProtocolDecoder.java b/src/org/traccar/protocol/TrvProtocolDecoder.java index 94796fa5e..8075515c2 100644 --- a/src/org/traccar/protocol/TrvProtocolDecoder.java +++ b/src/org/traccar/protocol/TrvProtocolDecoder.java @@ -105,7 +105,7 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder { position.set(Event.KEY_GSM, parser.nextInt()); position.set(Event.KEY_SATELLITES, parser.nextInt()); - position.set(Event.KEY_BATTERY , parser.nextInt()); + position.set(Event.KEY_BATTERY, parser.nextInt()); position.set(Event.KEY_IGNITION, parser.nextInt() != 0); position.set("arm", parser.nextInt()); diff --git a/src/org/traccar/protocol/TelikProtocol.java b/src/org/traccar/protocol/Tt8850Protocol.java index b6b5ba14d..a409205c7 100644 --- a/src/org/traccar/protocol/TelikProtocol.java +++ b/src/org/traccar/protocol/Tt8850Protocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,10 +24,10 @@ import org.traccar.TrackerServer; import java.util.List; -public class TelikProtocol extends BaseProtocol { +public class Tt8850Protocol extends BaseProtocol { - public TelikProtocol() { - super("telik"); + public Tt8850Protocol() { + super("tt8850"); } @Override @@ -35,9 +35,9 @@ public class TelikProtocol extends BaseProtocol { serverList.add(new TrackerServer(new ServerBootstrap(), this.getName()) { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { - pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '\0')); + pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, "$")); pipeline.addLast("stringDecoder", new StringDecoder()); - pipeline.addLast("objectDecoder", new TelikProtocolDecoder(TelikProtocol.this)); + pipeline.addLast("objectDecoder", new Tt8850ProtocolDecoder(Tt8850Protocol.this)); } }); } diff --git a/src/org/traccar/protocol/Tt8850ProtocolDecoder.java b/src/org/traccar/protocol/Tt8850ProtocolDecoder.java new file mode 100644 index 000000000..9979fd61d --- /dev/null +++ b/src/org/traccar/protocol/Tt8850ProtocolDecoder.java @@ -0,0 +1,101 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import org.jboss.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; +import org.traccar.helper.UnitsConverter; +import org.traccar.model.Event; +import org.traccar.model.Position; + +import java.net.SocketAddress; +import java.util.regex.Pattern; + +public class Tt8850ProtocolDecoder extends BaseProtocolDecoder { + + public Tt8850ProtocolDecoder(Tt8850Protocol protocol) { + super(protocol); + } + + private static final Pattern PATTERN = new PatternBuilder() + .binary("0004,") + .number("xxxx,") + .expression("[01],") + .expression("GT...,") + .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version + .expression("([^,]+),") // imei + .any() + .number("(d{1,2})?,") // gps accuracy + .number("(d{1,3}.d)?,") // speed + .number("(d{1,3})?,") // course + .number("(-?d{1,5}.d)?,") // altitude + .number("(-?d{1,3}.d{6}),") // longitude + .number("(-?d{1,2}.d{6}),") // latitude + .number("(dddd)(dd)(dd)") // date + .number("(dd)(dd)(dd),") // time + .number("(0ddd)?,") // mcc + .number("(0ddd)?,") // mnc + .number("(xxxx)?,") // lac + .number("(xxxx)?,") // cell + .any() + .number("(dddd)(dd)(dd)") // date + .number("(dd)(dd)(dd),") // time + .number("(xxxx)") + .compile(); + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + Parser parser = new Parser(PATTERN, (String) msg); + if (!parser.matches()) { + return null; + } + + Position position = new Position(); + position.setProtocol(getProtocolName()); + + if (!identify(parser.next(), channel, remoteAddress)) { + return null; + } + position.setDeviceId(getDeviceId()); + + position.setValid(parser.nextInt() < 20); + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble())); + position.setCourse(parser.nextDouble()); + position.setAltitude(parser.nextDouble()); + position.setLongitude(parser.nextDouble()); + position.setLatitude(parser.nextDouble()); + + DateBuilder dateBuilder = new DateBuilder() + .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); + + if (parser.hasNext(4)) { + position.set(Event.KEY_MCC, parser.nextInt()); + position.set(Event.KEY_MNC, parser.nextInt()); + position.set(Event.KEY_LAC, parser.nextInt(16)); + position.set(Event.KEY_CID, parser.nextInt(16)); + } + + return position; + } + +} diff --git a/src/org/traccar/protocol/UlbotechProtocolDecoder.java b/src/org/traccar/protocol/UlbotechProtocolDecoder.java index bf0a9cc76..a5c608dd3 100644 --- a/src/org/traccar/protocol/UlbotechProtocolDecoder.java +++ b/src/org/traccar/protocol/UlbotechProtocolDecoder.java @@ -60,8 +60,8 @@ public class UlbotechProtocolDecoder extends BaseProtocolDecoder { while (buf.readerIndex() < end) { int parameterLength = buf.getUnsignedByte(buf.readerIndex()) >> 4; - position.add(ObdDecoder.decode(buf.readUnsignedByte() & 0x0F, buf.readUnsignedByte(), - ChannelBuffers.hexDump(buf.readBytes(parameterLength - 2)))); + int mode = buf.readUnsignedByte() & 0x0F; + position.add(ObdDecoder.decode(mode, ChannelBuffers.hexDump(buf.readBytes(parameterLength - 1)))); } } diff --git a/src/org/traccar/protocol/WatchProtocol.java b/src/org/traccar/protocol/WatchProtocol.java index 3a10f9c59..1198f689e 100644 --- a/src/org/traccar/protocol/WatchProtocol.java +++ b/src/org/traccar/protocol/WatchProtocol.java @@ -37,8 +37,8 @@ public class WatchProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, ']')); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new WatchProtocolDecoder(WatchProtocol.this)); } }); diff --git a/src/org/traccar/protocol/WialonProtocol.java b/src/org/traccar/protocol/WialonProtocol.java index 6fe924c2d..d0d3aa446 100644 --- a/src/org/traccar/protocol/WialonProtocol.java +++ b/src/org/traccar/protocol/WialonProtocol.java @@ -37,8 +37,8 @@ public class WialonProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(4 * 1024)); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new WialonProtocolDecoder(WialonProtocol.this)); } }); diff --git a/src/org/traccar/protocol/XexunProtocolDecoder.java b/src/org/traccar/protocol/XexunProtocolDecoder.java index 0929ee99b..4d4831a20 100644 --- a/src/org/traccar/protocol/XexunProtocolDecoder.java +++ b/src/org/traccar/protocol/XexunProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2015 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2012 - 2016 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,8 +38,8 @@ public class XexunProtocolDecoder extends BaseProtocolDecoder { .expression("G[PN]RMC,") .number("(dd)(dd)(dd).(d+),") // time .expression("([AV]),") // validity - .number("(d+)(dd.d+),([NS]),") // latitude - .number("(d+)(dd.d+),([EW])?,") // longitude + .number("(d*?)(d?d.d+),([NS]),") // latitude + .number("(d*?)(d?d.d+),([EW])?,") // longitude .number("(d+.?d*),") // speed .number("(d+.?d*)?,") // course .number("(dd)(dd)(dd),") // date diff --git a/src/org/traccar/protocol/XirgoProtocol.java b/src/org/traccar/protocol/XirgoProtocol.java index 83c373175..f186c731f 100644 --- a/src/org/traccar/protocol/XirgoProtocol.java +++ b/src/org/traccar/protocol/XirgoProtocol.java @@ -37,8 +37,8 @@ public class XirgoProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, "##")); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new XirgoProtocolDecoder(XirgoProtocol.this)); } }); diff --git a/src/org/traccar/protocol/YwtProtocol.java b/src/org/traccar/protocol/YwtProtocol.java index e9cdb1615..3fa154ec0 100644 --- a/src/org/traccar/protocol/YwtProtocol.java +++ b/src/org/traccar/protocol/YwtProtocol.java @@ -37,8 +37,8 @@ public class YwtProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024)); - pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new YwtProtocolDecoder(YwtProtocol.this)); } }); diff --git a/src/org/traccar/web/AsyncServlet.java b/src/org/traccar/web/AsyncServlet.java index b10df5408..e4cb64c57 100644 --- a/src/org/traccar/web/AsyncServlet.java +++ b/src/org/traccar/web/AsyncServlet.java @@ -226,7 +226,7 @@ public class AsyncServlet extends BaseServlet { synchronized (ASYNC_SESSIONS) { if (Boolean.parseBoolean(req.getParameter("first")) || !ASYNC_SESSIONS.containsKey(userId)) { - Collection<Long> devices = Context.getPermissionsManager().allowedDevices(userId); + Collection<Long> devices = Context.getPermissionsManager().getDevicePermissions(userId); ASYNC_SESSIONS.put(userId, new AsyncSession(userId, devices)); } diff --git a/src/org/traccar/web/BaseServlet.java b/src/org/traccar/web/BaseServlet.java index 8b022d556..17f4f19cd 100644 --- a/src/org/traccar/web/BaseServlet.java +++ b/src/org/traccar/web/BaseServlet.java @@ -53,7 +53,7 @@ public abstract class BaseServlet extends HttpServlet { String origin = req.getHeader(HttpHeaders.Names.ORIGIN); String allowed = Context.getConfig().getString("web.origin"); - if (allowed == null) { + if (allowed == null || origin == null) { resp.setHeader(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_ORIGIN, ALLOW_ORIGIN_VALUE); } else if (allowed.contains(origin)) { String originSafe = URLEncoder.encode(origin, StandardCharsets.UTF_8.name()); diff --git a/src/org/traccar/web/WebServer.java b/src/org/traccar/web/WebServer.java index d6cd19d87..4e764d5d6 100644 --- a/src/org/traccar/web/WebServer.java +++ b/src/org/traccar/web/WebServer.java @@ -42,7 +42,9 @@ import org.traccar.api.ResourceErrorHandler; import org.traccar.api.SecurityRequestFilter; import org.traccar.api.resource.CommandResource; import org.traccar.api.resource.DeviceResource; -import org.traccar.api.resource.PermissionResource; +import org.traccar.api.resource.DevicePermissionResource; +import org.traccar.api.resource.GroupPermissionResource; +import org.traccar.api.resource.GroupResource; import org.traccar.api.resource.PositionResource; import org.traccar.api.resource.ServerResource; import org.traccar.api.resource.SessionResource; @@ -140,7 +142,8 @@ public class WebServer { resourceConfig.register(SecurityRequestFilter.class); resourceConfig.register(CorsResponseFilter.class); resourceConfig.registerClasses(ServerResource.class, SessionResource.class, CommandResource.class, - PermissionResource.class, DeviceResource.class, UserResource.class, PositionResource.class); + GroupPermissionResource.class, DevicePermissionResource.class, UserResource.class, + GroupResource.class, DeviceResource.class, PositionResource.class); servletHandler.addServlet(new ServletHolder(new ServletContainer(resourceConfig)), "/*"); handlers.addHandler(servletHandler); |