diff options
163 files changed, 6547 insertions, 2826 deletions
diff --git a/build.gradle b/build.gradle index 5e4fc3868..a63ff805e 100644 --- a/build.gradle +++ b/build.gradle @@ -76,7 +76,7 @@ dependencies { implementation "com.sun.xml.bind:jaxb-core:2.3.0.1" implementation "com.sun.xml.bind:jaxb-impl:2.3.3" implementation "javax.activation:activation:1.1.1" - testImplementation "junit:junit:4.13" + testImplementation "junit:junit:4.13.1" } task copyDependencies(type: Copy) { @@ -22,7 +22,7 @@ <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> - <version>4.13</version> + <version>4.13.1</version> <scope>test</scope> </dependency> <dependency> diff --git a/schema/changelog-4.11.xml b/schema/changelog-4.11.xml new file mode 100644 index 000000000..15916a8ba --- /dev/null +++ b/schema/changelog-4.11.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<databaseChangeLog + xmlns="http://www.liquibase.org/xml/ns/dbchangelog" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog + http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd" + logicalFilePath="changelog-4.11"> + + <changeSet author="author" id="changelog-4.11"> + + <addColumn tableName="tc_servers"> + <column name="announcement" type="VARCHAR(4000)" /> + </addColumn> + + </changeSet> + +</databaseChangeLog> diff --git a/schema/changelog-master.xml b/schema/changelog-master.xml index 5e89e746b..7d4475df7 100644 --- a/schema/changelog-master.xml +++ b/schema/changelog-master.xml @@ -26,5 +26,6 @@ <include file="changelog-4.7.xml" relativeToChangelogFile="true" /> <include file="changelog-4.9.xml" relativeToChangelogFile="true" /> <include file="changelog-4.10.xml" relativeToChangelogFile="true" /> + <include file="changelog-4.11.xml" relativeToChangelogFile="true" /> </databaseChangeLog> diff --git a/setup/default.xml b/setup/default.xml index 7e1572530..eb7b538ca 100644 --- a/setup/default.xml +++ b/setup/default.xml @@ -14,7 +14,6 @@ <entry key='web.port'>8082</entry> <entry key='web.path'>./web</entry> <entry key='web.cacheControl'>max-age=3600,public</entry> - <entry key='web.healthCheck'>true</entry> <entry key='geocoder.enable'>false</entry> <entry key='geocoder.type'>google</entry> @@ -289,5 +288,9 @@ <entry key='wli.port'>5209</entry> <entry key='niot.port'>5210</entry> <entry key='portman.port'>5211</entry> + <entry key='moovbox.port'>5212</entry> + <entry key='futureway.port'>5213</entry> + <entry key='polte.port'>5214</entry> + <entry key='net.port'>5215</entry> </properties> diff --git a/setup/package.sh b/setup/package.sh index 8014aca36..093db481b 100755 --- a/setup/package.sh +++ b/setup/package.sh @@ -145,7 +145,7 @@ package_linux () { unzip -q -o jdk-*-linux-$1.zip jlink --module-path jdk-*-linux-$1/jmods --add-modules java.se,jdk.charsets,jdk.crypto.ec --output out/jre rm -rf jdk-*-linux-$1 - makeself --quiet --notemp out traccar.run "traccar" ./setup.sh + makeself --needroot --quiet --notemp out traccar.run "traccar" ./setup.sh rm -rf out/jre zip -q -j traccar-linux-$2-$VERSION.zip traccar.run README.txt diff --git a/setup/setup.sh b/setup/setup.sh index 7443c3b4e..1b07990ae 100755 --- a/setup/setup.sh +++ b/setup/setup.sh @@ -1,9 +1,21 @@ #!/bin/sh +PRESERVECONFIG=0 +if [ -f /opt/traccar/conf/traccar.xml ] +then + cp /opt/traccar/conf/traccar.xml /opt/traccar/conf/traccar.xml.saved + PRESERVECONFIG=1 +fi + mkdir -p /opt/traccar cp -r * /opt/traccar chmod -R go+rX /opt/traccar +if [ ${PRESERVECONFIG} -eq 1 ] && [ -f /opt/traccar/conf/traccar.xml.saved ] +then + mv -f /opt/traccar/conf/traccar.xml.saved /opt/traccar/conf/traccar.xml +fi + mv /opt/traccar/traccar.service /etc/systemd/system chmod 664 /etc/systemd/system/traccar.service diff --git a/setup/traccar.service b/setup/traccar.service index 4fb87a6cd..6c11ea1ea 100644 --- a/setup/traccar.service +++ b/setup/traccar.service @@ -8,7 +8,7 @@ WorkingDirectory=/opt/traccar ExecStart=/opt/traccar/jre/bin/java -jar tracker-server.jar conf/traccar.xml SyslogIdentifier=traccar SuccessExitStatus=143 -WatchdogSec=300 +WatchdogSec=600 Restart=on-failure RestartSec=10 diff --git a/src/main/java/org/traccar/BasePipelineFactory.java b/src/main/java/org/traccar/BasePipelineFactory.java index 5a5850a86..ffce45342 100644 --- a/src/main/java/org/traccar/BasePipelineFactory.java +++ b/src/main/java/org/traccar/BasePipelineFactory.java @@ -26,6 +26,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.config.Keys; import org.traccar.handler.DefaultDataHandler; +import org.traccar.handler.SpeedLimitHandler; import org.traccar.handler.TimeHandler; import org.traccar.handler.events.AlertEventHandler; import org.traccar.handler.events.CommandResultEventHandler; @@ -58,7 +59,7 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> { private final TrackerServer server; private final String protocol; - private boolean eventsEnabled; + private final boolean eventsEnabled; private int timeout; public BasePipelineFactory(TrackerServer server, String protocol) { @@ -133,6 +134,7 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> { pipeline, FilterHandler.class, GeocoderHandler.class, + SpeedLimitHandler.class, MotionHandler.class, CopyAttributesHandler.class, EngineHoursHandler.class, diff --git a/src/main/java/org/traccar/BaseProtocolDecoder.java b/src/main/java/org/traccar/BaseProtocolDecoder.java index 99f3bc627..50e80a457 100644 --- a/src/main/java/org/traccar/BaseProtocolDecoder.java +++ b/src/main/java/org/traccar/BaseProtocolDecoder.java @@ -104,7 +104,7 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder { } private DeviceSession channelDeviceSession; // connection-based protocols - private Map<SocketAddress, DeviceSession> addressDeviceSessions = new HashMap<>(); // connectionless protocols + private final Map<SocketAddress, DeviceSession> addressDeviceSessions = new HashMap<>(); // connectionless protocols private long findDeviceId(SocketAddress remoteAddress, String... uniqueIds) { if (uniqueIds.length > 0) { diff --git a/src/main/java/org/traccar/Context.java b/src/main/java/org/traccar/Context.java index 9c20db9e4..af7e03837 100644 --- a/src/main/java/org/traccar/Context.java +++ b/src/main/java/org/traccar/Context.java @@ -40,6 +40,7 @@ import org.traccar.database.MaintenancesManager; import org.traccar.database.MediaManager; import org.traccar.database.NotificationManager; import org.traccar.database.PermissionsManager; +import org.traccar.schedule.ScheduleManager; import org.traccar.database.UsersManager; import org.traccar.geocoder.Geocoder; import org.traccar.helper.Log; @@ -165,6 +166,12 @@ public final class Context { return serverManager; } + private static ScheduleManager scheduleManager; + + public static ScheduleManager getScheduleManager() { + return scheduleManager; + } + private static GeofenceManager geofenceManager; public static GeofenceManager getGeofenceManager() { @@ -332,6 +339,7 @@ public final class Context { } serverManager = new ServerManager(); + scheduleManager = new ScheduleManager(); if (config.getBoolean("event.forward.enable")) { eventForwarder = new JsonTypeEventForwarder(); diff --git a/src/main/java/org/traccar/ExtendedObjectDecoder.java b/src/main/java/org/traccar/ExtendedObjectDecoder.java index 681924e87..46720da52 100644 --- a/src/main/java/org/traccar/ExtendedObjectDecoder.java +++ b/src/main/java/org/traccar/ExtendedObjectDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org) * * 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 io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.util.ReferenceCountUtil; +import org.traccar.config.Keys; import org.traccar.helper.DataConverter; import org.traccar.model.Position; @@ -31,14 +32,14 @@ import java.util.Collection; public abstract class ExtendedObjectDecoder extends ChannelInboundHandlerAdapter { private void saveOriginal(Object decodedMessage, Object originalMessage) { - if (Context.getConfig().getBoolean("database.saveOriginal") && decodedMessage instanceof Position) { + if (Context.getConfig().getBoolean(Keys.DATABASE_SAVE_ORIGINAL) && decodedMessage instanceof Position) { Position position = (Position) decodedMessage; if (originalMessage instanceof ByteBuf) { ByteBuf buf = (ByteBuf) originalMessage; - position.set(Position.KEY_ORIGINAL, ByteBufUtil.hexDump(buf)); + position.set(Position.KEY_ORIGINAL, ByteBufUtil.hexDump(buf, 0, buf.writerIndex())); } else if (originalMessage instanceof String) { position.set(Position.KEY_ORIGINAL, DataConverter.printHex( - ((String) originalMessage).getBytes(StandardCharsets.US_ASCII))); + ((String) originalMessage).getBytes(StandardCharsets.US_ASCII))); } } } diff --git a/src/main/java/org/traccar/Main.java b/src/main/java/org/traccar/Main.java index a0d93dbc8..59afab3a5 100644 --- a/src/main/java/org/traccar/Main.java +++ b/src/main/java/org/traccar/Main.java @@ -144,6 +144,7 @@ public final class Main { if (Context.getWebServer() != null) { Context.getWebServer().start(); } + Context.getScheduleManager().start(); scheduleHealthCheck(); scheduleDatabaseCleanup(); @@ -153,6 +154,7 @@ public final class Main { Runtime.getRuntime().addShutdownHook(new Thread(() -> { LOGGER.info("Shutting down server..."); + Context.getScheduleManager().stop(); if (Context.getWebServer() != null) { Context.getWebServer().stop(); } diff --git a/src/main/java/org/traccar/MainModule.java b/src/main/java/org/traccar/MainModule.java index de6ec5b87..e751cb453 100644 --- a/src/main/java/org/traccar/MainModule.java +++ b/src/main/java/org/traccar/MainModule.java @@ -61,6 +61,7 @@ import org.traccar.handler.GeolocationHandler; import org.traccar.handler.HemisphereHandler; import org.traccar.handler.MotionHandler; import org.traccar.handler.RemoteAddressHandler; +import org.traccar.handler.SpeedLimitHandler; import org.traccar.handler.TimeHandler; import org.traccar.handler.events.AlertEventHandler; import org.traccar.handler.events.CommandResultEventHandler; @@ -76,6 +77,8 @@ import org.traccar.reports.model.TripsConfig; import javax.annotation.Nullable; import javax.ws.rs.client.Client; import io.netty.util.Timer; +import org.traccar.speedlimit.OverpassSpeedLimitProvider; +import org.traccar.speedlimit.SpeedLimitProvider; public class MainModule extends AbstractModule { @@ -211,6 +214,21 @@ public class MainModule extends AbstractModule { @Singleton @Provides + public static SpeedLimitProvider provideSpeedLimitProvider(Config config) { + if (config.getBoolean(Keys.SPEED_LIMIT_ENABLE)) { + String type = config.getString(Keys.SPEED_LIMIT_TYPE, "overpass"); + String url = config.getString(Keys.SPEED_LIMIT_URL); + switch (type) { + case "overpass": + default: + return new OverpassSpeedLimitProvider(url); + } + } + return null; + } + + @Singleton + @Provides public static DistanceHandler provideDistanceHandler(Config config, IdentityManager identityManager) { return new DistanceHandler(config, identityManager); } @@ -275,6 +293,15 @@ public class MainModule extends AbstractModule { @Singleton @Provides + public static SpeedLimitHandler provideSpeedLimitHandler(@Nullable SpeedLimitProvider speedLimitProvider) { + if (speedLimitProvider != null) { + return new SpeedLimitHandler(speedLimitProvider); + } + return null; + } + + @Singleton + @Provides public static MotionHandler provideMotionHandler(TripsConfig tripsConfig) { return new MotionHandler(tripsConfig.getSpeedThreshold()); } diff --git a/src/main/java/org/traccar/WebDataHandler.java b/src/main/java/org/traccar/WebDataHandler.java index 3be575b48..a7ea0b2a5 100644 --- a/src/main/java/org/traccar/WebDataHandler.java +++ b/src/main/java/org/traccar/WebDataHandler.java @@ -74,7 +74,7 @@ public class WebDataHandler extends BaseDataHandler { private final int retryCount; private final int retryLimit; - private AtomicInteger deliveryPending; + private final AtomicInteger deliveryPending; @Inject public WebDataHandler( @@ -117,7 +117,7 @@ public class WebDataHandler extends BaseDataHandler { f.format("%1$td%1$tm%1$ty,,", calendar); } - s.append(Checksum.nmea(s.toString())); + s.append(Checksum.nmea(s.substring(1))); return s.toString(); } diff --git a/src/main/java/org/traccar/WindowsService.java b/src/main/java/org/traccar/WindowsService.java index 4a8955608..831cd3ebc 100644 --- a/src/main/java/org/traccar/WindowsService.java +++ b/src/main/java/org/traccar/WindowsService.java @@ -38,16 +38,14 @@ public abstract class WindowsService { private final Object waitObject = new Object(); - private String serviceName; - private ServiceMain serviceMain; - private ServiceControl serviceControl; + private final String serviceName; private SERVICE_STATUS_HANDLE serviceStatusHandle; public WindowsService(String serviceName) { this.serviceName = serviceName; } - public boolean install( + public void install( String displayName, String description, String[] dependencies, String account, String password, String config) throws URISyntaxException { @@ -60,7 +58,6 @@ public abstract class WindowsService { + " -jar \"" + jar.getAbsolutePath() + "\"" + " --service \"" + config + "\""; - boolean success = false; StringBuilder dep = new StringBuilder(); if (dependencies != null) { @@ -84,29 +81,25 @@ public abstract class WindowsService { null, null, dep.toString(), account, password); if (service != null) { - success = ADVAPI_32.ChangeServiceConfig2(service, Winsvc.SERVICE_CONFIG_DESCRIPTION, desc); + ADVAPI_32.ChangeServiceConfig2(service, Winsvc.SERVICE_CONFIG_DESCRIPTION, desc); ADVAPI_32.CloseServiceHandle(service); } ADVAPI_32.CloseServiceHandle(serviceManager); } - return success; } - public boolean uninstall() { - boolean success = false; - + public void uninstall() { SC_HANDLE serviceManager = openServiceControlManager(null, Winsvc.SC_MANAGER_ALL_ACCESS); if (serviceManager != null) { SC_HANDLE service = ADVAPI_32.OpenService(serviceManager, serviceName, Winsvc.SERVICE_ALL_ACCESS); if (service != null) { - success = ADVAPI_32.DeleteService(service); + ADVAPI_32.DeleteService(service); ADVAPI_32.CloseServiceHandle(service); } ADVAPI_32.CloseServiceHandle(serviceManager); } - return success; } public boolean start() { @@ -152,7 +145,7 @@ public abstract class WindowsService { POSIXFactory.getPOSIX().chdir(path); - serviceMain = new ServiceMain(); + ServiceMain serviceMain = new ServiceMain(); SERVICE_TABLE_ENTRY entry = new SERVICE_TABLE_ENTRY(); entry.lpServiceName = serviceName; entry.lpServiceProc = serviceMain; @@ -180,7 +173,7 @@ public abstract class WindowsService { private class ServiceMain implements SERVICE_MAIN_FUNCTION { public void callback(int dwArgc, Pointer lpszArgv) { - serviceControl = new ServiceControl(); + ServiceControl serviceControl = new ServiceControl(); serviceStatusHandle = ADVAPI_32.RegisterServiceCtrlHandlerEx(serviceName, serviceControl, null); reportStatus(Winsvc.SERVICE_START_PENDING, WinError.NO_ERROR, 3000); @@ -194,7 +187,8 @@ public abstract class WindowsService { synchronized (waitObject) { waitObject.wait(); } - } catch (InterruptedException ex) { + } catch (InterruptedException e) { + e.printStackTrace(); } reportStatus(Winsvc.SERVICE_STOPPED, WinError.NO_ERROR, 0); diff --git a/src/main/java/org/traccar/WrapperInboundHandler.java b/src/main/java/org/traccar/WrapperInboundHandler.java index ca33d021f..5e2b1d304 100644 --- a/src/main/java/org/traccar/WrapperInboundHandler.java +++ b/src/main/java/org/traccar/WrapperInboundHandler.java @@ -20,7 +20,7 @@ import io.netty.channel.ChannelInboundHandler; public class WrapperInboundHandler implements ChannelInboundHandler { - private ChannelInboundHandler handler; + private final ChannelInboundHandler handler; public ChannelInboundHandler getWrappedHandler() { return handler; diff --git a/src/main/java/org/traccar/WrapperOutboundHandler.java b/src/main/java/org/traccar/WrapperOutboundHandler.java index 0136c5b22..ae2b06ad2 100644 --- a/src/main/java/org/traccar/WrapperOutboundHandler.java +++ b/src/main/java/org/traccar/WrapperOutboundHandler.java @@ -23,7 +23,7 @@ import java.net.SocketAddress; public class WrapperOutboundHandler implements ChannelOutboundHandler { - private ChannelOutboundHandler handler; + private final ChannelOutboundHandler handler; public ChannelOutboundHandler getWrappedHandler() { return handler; diff --git a/src/main/java/org/traccar/api/AsyncSocket.java b/src/main/java/org/traccar/api/AsyncSocket.java index 906d16b5b..b2ff5031a 100644 --- a/src/main/java/org/traccar/api/AsyncSocket.java +++ b/src/main/java/org/traccar/api/AsyncSocket.java @@ -39,7 +39,7 @@ public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.U private static final String KEY_POSITIONS = "positions"; private static final String KEY_EVENTS = "events"; - private long userId; + private final long userId; public AsyncSocket(long userId) { this.userId = userId; diff --git a/src/main/java/org/traccar/api/AsyncSocketServlet.java b/src/main/java/org/traccar/api/AsyncSocketServlet.java index 9318b6fc6..51fbfb478 100644 --- a/src/main/java/org/traccar/api/AsyncSocketServlet.java +++ b/src/main/java/org/traccar/api/AsyncSocketServlet.java @@ -15,9 +15,6 @@ */ package org.traccar.api; -import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest; -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; @@ -30,15 +27,12 @@ public class AsyncSocketServlet extends WebSocketServlet { @Override public void configure(WebSocketServletFactory factory) { factory.getPolicy().setIdleTimeout(Context.getConfig().getLong("web.timeout", ASYNC_TIMEOUT)); - factory.setCreator(new WebSocketCreator() { - @Override - public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp) { - if (req.getSession() != null) { - long userId = (Long) req.getSession().getAttribute(SessionResource.USER_ID_KEY); - return new AsyncSocket(userId); - } else { - return null; - } + factory.setCreator((req, resp) -> { + 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/main/java/org/traccar/api/BaseObjectResource.java b/src/main/java/org/traccar/api/BaseObjectResource.java index c4206f385..bc7d3c466 100644 --- a/src/main/java/org/traccar/api/BaseObjectResource.java +++ b/src/main/java/org/traccar/api/BaseObjectResource.java @@ -44,7 +44,7 @@ import org.traccar.model.User; public abstract class BaseObjectResource<T extends BaseModel> extends BaseResource { - private Class<T> baseClass; + private final Class<T> baseClass; public BaseObjectResource(Class<T> baseClass) { this.baseClass = baseClass; diff --git a/src/main/java/org/traccar/api/DateParameterConverterProvider.java b/src/main/java/org/traccar/api/DateParameterConverterProvider.java new file mode 100644 index 000000000..f07ece984 --- /dev/null +++ b/src/main/java/org/traccar/api/DateParameterConverterProvider.java @@ -0,0 +1,52 @@ +/* + * Copyright 2020 Anton Tananaev (anton@traccar.org) + * + * 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; + +import org.traccar.helper.DateUtil; + +import javax.ws.rs.ext.ParamConverter; +import javax.ws.rs.ext.ParamConverterProvider; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Date; + +public class DateParameterConverterProvider implements ParamConverterProvider { + + public static class DateParameterConverter implements ParamConverter<Date> { + + @Override + public Date fromString(String value) { + return value != null ? DateUtil.parseDate(value) : null; + } + + @Override + public String toString(Date value) { + return value != null ? DateUtil.formatDate(value) : null; + } + + } + + @Override + public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) { + if (Date.class.equals(rawType)) { + @SuppressWarnings("unchecked") + ParamConverter<T> paramConverter = (ParamConverter<T>) new DateParameterConverter(); + return paramConverter; + } + return null; + } + +} diff --git a/src/main/java/org/traccar/api/HealthCheckService.java b/src/main/java/org/traccar/api/HealthCheckService.java index 1e8f0d731..c9ce5a6c9 100644 --- a/src/main/java/org/traccar/api/HealthCheckService.java +++ b/src/main/java/org/traccar/api/HealthCheckService.java @@ -20,6 +20,7 @@ import com.sun.jna.Native; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.Context; +import org.traccar.config.Keys; import java.util.TimerTask; @@ -33,7 +34,7 @@ public class HealthCheckService { private long period; public HealthCheckService() { - if (Context.getConfig().getBoolean("web.healthCheck") + if (!Context.getConfig().getBoolean(Keys.WEB_DISABLE_HEALTH_CHECK) && System.getProperty("os.name").toLowerCase().startsWith("linux")) { try { systemD = Native.load("systemd", SystemD.class); diff --git a/src/main/java/org/traccar/api/UserPrincipal.java b/src/main/java/org/traccar/api/UserPrincipal.java index 80e92c2dd..175bca391 100644 --- a/src/main/java/org/traccar/api/UserPrincipal.java +++ b/src/main/java/org/traccar/api/UserPrincipal.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ import java.security.Principal; public class UserPrincipal implements Principal { - private long userId; + private final long userId; public UserPrincipal(long userId) { this.userId = userId; diff --git a/src/main/java/org/traccar/api/resource/DeviceResource.java b/src/main/java/org/traccar/api/resource/DeviceResource.java index f9c9a139d..7006cdb84 100644 --- a/src/main/java/org/traccar/api/resource/DeviceResource.java +++ b/src/main/java/org/traccar/api/resource/DeviceResource.java @@ -52,7 +52,7 @@ public class DeviceResource extends BaseObjectResource<Device> { @QueryParam("uniqueId") List<String> uniqueIds, @QueryParam("id") List<Long> deviceIds) throws SQLException { DeviceManager deviceManager = Context.getDeviceManager(); - Set<Long> result = null; + Set<Long> result; if (all) { if (Context.getPermissionsManager().getUserAdmin(getUserId())) { result = deviceManager.getAllItems(); diff --git a/src/main/java/org/traccar/api/resource/PositionResource.java b/src/main/java/org/traccar/api/resource/PositionResource.java index 67aa6dd32..e93feaccf 100644 --- a/src/main/java/org/traccar/api/resource/PositionResource.java +++ b/src/main/java/org/traccar/api/resource/PositionResource.java @@ -17,7 +17,6 @@ package org.traccar.api.resource; import org.traccar.Context; import org.traccar.api.BaseResource; -import org.traccar.helper.DateUtil; import org.traccar.model.Position; import org.traccar.web.CsvBuilder; import org.traccar.web.GpxBuilder; @@ -35,6 +34,7 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Date; import java.util.List; @Path("positions") @@ -50,7 +50,7 @@ public class PositionResource extends BaseResource { @GET public Collection<Position> getJson( @QueryParam("deviceId") long deviceId, @QueryParam("id") List<Long> positionIds, - @QueryParam("from") String from, @QueryParam("to") String to) + @QueryParam("from") Date from, @QueryParam("to") Date to) throws SQLException { if (!positionIds.isEmpty()) { ArrayList<Position> positions = new ArrayList<>(); @@ -65,8 +65,7 @@ public class PositionResource extends BaseResource { } else { Context.getPermissionsManager().checkDevice(getUserId(), deviceId); if (from != null && to != null) { - return Context.getDataManager().getPositions( - deviceId, DateUtil.parseDate(from), DateUtil.parseDate(to)); + return Context.getDataManager().getPositions(deviceId, from, to); } else { return Collections.singleton(Context.getDeviceManager().getLastPosition(deviceId)); } @@ -76,25 +75,23 @@ public class PositionResource extends BaseResource { @GET @Produces(TEXT_CSV) public Response getCsv( - @QueryParam("deviceId") long deviceId, @QueryParam("from") String from, @QueryParam("to") String to) + @QueryParam("deviceId") long deviceId, @QueryParam("from") Date from, @QueryParam("to") Date to) throws SQLException { Context.getPermissionsManager().checkDevice(getUserId(), deviceId); CsvBuilder csv = new CsvBuilder(); csv.addHeaderLine(new Position()); - csv.addArray(Context.getDataManager().getPositions( - deviceId, DateUtil.parseDate(from), DateUtil.parseDate(to))); + csv.addArray(Context.getDataManager().getPositions(deviceId, from, to)); return Response.ok(csv.build()).header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_CSV).build(); } @GET @Produces(GPX) public Response getGpx( - @QueryParam("deviceId") long deviceId, @QueryParam("from") String from, @QueryParam("to") String to) + @QueryParam("deviceId") long deviceId, @QueryParam("from") Date from, @QueryParam("to") Date to) throws SQLException { Context.getPermissionsManager().checkDevice(getUserId(), deviceId); GpxBuilder gpx = new GpxBuilder(Context.getIdentityManager().getById(deviceId).getName()); - gpx.addPositions(Context.getDataManager().getPositions( - deviceId, DateUtil.parseDate(from), DateUtil.parseDate(to))); + gpx.addPositions(Context.getDataManager().getPositions(deviceId, from, to)); return Response.ok(gpx.build()).header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_GPX).build(); } diff --git a/src/main/java/org/traccar/api/resource/ReportResource.java b/src/main/java/org/traccar/api/resource/ReportResource.java index d371cf987..7347bfd64 100644 --- a/src/main/java/org/traccar/api/resource/ReportResource.java +++ b/src/main/java/org/traccar/api/resource/ReportResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org) * Copyright 2016 - 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,6 +20,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.sql.SQLException; import java.util.Collection; +import java.util.Date; import java.util.List; import javax.activation.DataHandler; @@ -39,7 +40,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.Context; import org.traccar.api.BaseResource; -import org.traccar.helper.DateUtil; +import org.traccar.helper.LogAction; import org.traccar.model.Event; import org.traccar.model.Position; import org.traccar.reports.Events; @@ -97,9 +98,9 @@ public class ReportResource extends BaseResource { @GET public Collection<Position> getRoute( @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, - @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException { - return Route.getObjects(getUserId(), deviceIds, groupIds, - DateUtil.parseDate(from), DateUtil.parseDate(to)); + @QueryParam("from") Date from, @QueryParam("to") Date to) throws SQLException { + LogAction.logReport(getUserId(), "route", from, to, deviceIds, groupIds); + return Route.getObjects(getUserId(), deviceIds, groupIds, from, to); } @Path("route") @@ -107,11 +108,11 @@ public class ReportResource extends BaseResource { @Produces(XLSX) public Response getRouteExcel( @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, - @QueryParam("from") String from, @QueryParam("to") String to, @QueryParam("mail") boolean mail) + @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("mail") boolean mail) throws SQLException, IOException { return executeReport(getUserId(), mail, stream -> { - Route.getExcel(stream, getUserId(), deviceIds, groupIds, - DateUtil.parseDate(from), DateUtil.parseDate(to)); + LogAction.logReport(getUserId(), "route", from, to, deviceIds, groupIds); + Route.getExcel(stream, getUserId(), deviceIds, groupIds, from, to); }); } @@ -120,9 +121,9 @@ public class ReportResource extends BaseResource { public Collection<Event> getEvents( @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, @QueryParam("type") final List<String> types, - @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException { - return Events.getObjects(getUserId(), deviceIds, groupIds, types, - DateUtil.parseDate(from), DateUtil.parseDate(to)); + @QueryParam("from") Date from, @QueryParam("to") Date to) throws SQLException { + LogAction.logReport(getUserId(), "events", from, to, deviceIds, groupIds); + return Events.getObjects(getUserId(), deviceIds, groupIds, types, from, to); } @Path("events") @@ -131,11 +132,11 @@ public class ReportResource extends BaseResource { public Response getEventsExcel( @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, @QueryParam("type") final List<String> types, - @QueryParam("from") String from, @QueryParam("to") String to, @QueryParam("mail") boolean mail) + @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("mail") boolean mail) throws SQLException, IOException { return executeReport(getUserId(), mail, stream -> { - Events.getExcel(stream, getUserId(), deviceIds, groupIds, types, - DateUtil.parseDate(from), DateUtil.parseDate(to)); + LogAction.logReport(getUserId(), "events", from, to, deviceIds, groupIds); + Events.getExcel(stream, getUserId(), deviceIds, groupIds, types, from, to); }); } @@ -143,9 +144,10 @@ public class ReportResource extends BaseResource { @GET public Collection<SummaryReport> getSummary( @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, - @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException { - return Summary.getObjects(getUserId(), deviceIds, groupIds, - DateUtil.parseDate(from), DateUtil.parseDate(to)); + @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("daily") boolean daily) + throws SQLException { + LogAction.logReport(getUserId(), "summary", from, to, deviceIds, groupIds); + return Summary.getObjects(getUserId(), deviceIds, groupIds, from, to, daily); } @Path("summary") @@ -153,11 +155,12 @@ public class ReportResource extends BaseResource { @Produces(XLSX) public Response getSummaryExcel( @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, - @QueryParam("from") String from, @QueryParam("to") String to, @QueryParam("mail") boolean mail) + @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("daily") boolean daily, + @QueryParam("mail") boolean mail) throws SQLException, IOException { return executeReport(getUserId(), mail, stream -> { - Summary.getExcel(stream, getUserId(), deviceIds, groupIds, - DateUtil.parseDate(from), DateUtil.parseDate(to)); + LogAction.logReport(getUserId(), "summary", from, to, deviceIds, groupIds); + Summary.getExcel(stream, getUserId(), deviceIds, groupIds, from, to, daily); }); } @@ -166,9 +169,9 @@ public class ReportResource extends BaseResource { @Produces(MediaType.APPLICATION_JSON) public Collection<TripReport> getTrips( @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, - @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException { - return Trips.getObjects(getUserId(), deviceIds, groupIds, - DateUtil.parseDate(from), DateUtil.parseDate(to)); + @QueryParam("from") Date from, @QueryParam("to") Date to) throws SQLException { + LogAction.logReport(getUserId(), "trips", from, to, deviceIds, groupIds); + return Trips.getObjects(getUserId(), deviceIds, groupIds, from, to); } @Path("trips") @@ -176,11 +179,11 @@ public class ReportResource extends BaseResource { @Produces(XLSX) public Response getTripsExcel( @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, - @QueryParam("from") String from, @QueryParam("to") String to, @QueryParam("mail") boolean mail) + @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("mail") boolean mail) throws SQLException, IOException { return executeReport(getUserId(), mail, stream -> { - Trips.getExcel(stream, getUserId(), deviceIds, groupIds, - DateUtil.parseDate(from), DateUtil.parseDate(to)); + LogAction.logReport(getUserId(), "trips", from, to, deviceIds, groupIds); + Trips.getExcel(stream, getUserId(), deviceIds, groupIds, from, to); }); } @@ -189,9 +192,9 @@ public class ReportResource extends BaseResource { @Produces(MediaType.APPLICATION_JSON) public Collection<StopReport> getStops( @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, - @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException { - return Stops.getObjects(getUserId(), deviceIds, groupIds, - DateUtil.parseDate(from), DateUtil.parseDate(to)); + @QueryParam("from") Date from, @QueryParam("to") Date to) throws SQLException { + LogAction.logReport(getUserId(), "stops", from, to, deviceIds, groupIds); + return Stops.getObjects(getUserId(), deviceIds, groupIds, from, to); } @Path("stops") @@ -199,11 +202,11 @@ public class ReportResource extends BaseResource { @Produces(XLSX) public Response getStopsExcel( @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, - @QueryParam("from") String from, @QueryParam("to") String to, @QueryParam("mail") boolean mail) + @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("mail") boolean mail) throws SQLException, IOException { return executeReport(getUserId(), mail, stream -> { - Stops.getExcel(stream, getUserId(), deviceIds, groupIds, - DateUtil.parseDate(from), DateUtil.parseDate(to)); + LogAction.logReport(getUserId(), "stops", from, to, deviceIds, groupIds); + Stops.getExcel(stream, getUserId(), deviceIds, groupIds, from, to); }); } diff --git a/src/main/java/org/traccar/api/resource/ServerResource.java b/src/main/java/org/traccar/api/resource/ServerResource.java index e7cad2a0c..91488afff 100644 --- a/src/main/java/org/traccar/api/resource/ServerResource.java +++ b/src/main/java/org/traccar/api/resource/ServerResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org) * * 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,12 @@ public class ServerResource extends BaseResource { @PermitAll @GET - public Server get() throws SQLException { - return Context.getPermissionsManager().getServer(); + public Server get(@QueryParam("force") boolean force) throws SQLException { + if (force) { + return Context.getDataManager().getServer(); + } else { + return Context.getPermissionsManager().getServer(); + } } @PUT diff --git a/src/main/java/org/traccar/api/resource/StatisticsResource.java b/src/main/java/org/traccar/api/resource/StatisticsResource.java index e801d4ff3..58073e7d1 100644 --- a/src/main/java/org/traccar/api/resource/StatisticsResource.java +++ b/src/main/java/org/traccar/api/resource/StatisticsResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org) * * 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,6 @@ package org.traccar.api.resource; import org.traccar.Context; import org.traccar.api.BaseResource; -import org.traccar.helper.DateUtil; import org.traccar.model.Statistics; import javax.ws.rs.Consumes; @@ -28,6 +27,7 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import java.sql.SQLException; import java.util.Collection; +import java.util.Date; @Path("statistics") @Produces(MediaType.APPLICATION_JSON) @@ -36,9 +36,9 @@ public class StatisticsResource extends BaseResource { @GET public Collection<Statistics> get( - @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException { + @QueryParam("from") Date from, @QueryParam("to") Date to) throws SQLException { Context.getPermissionsManager().checkAdmin(getUserId()); - return Context.getDataManager().getStatistics(DateUtil.parseDate(from), DateUtil.parseDate(to)); + return Context.getDataManager().getStatistics(from, to); } } diff --git a/src/main/java/org/traccar/api/resource/UserResource.java b/src/main/java/org/traccar/api/resource/UserResource.java index 0b42d8d92..813ace6d6 100644 --- a/src/main/java/org/traccar/api/resource/UserResource.java +++ b/src/main/java/org/traccar/api/resource/UserResource.java @@ -48,7 +48,7 @@ public class UserResource extends BaseObjectResource<User> { @GET public Collection<User> get(@QueryParam("userId") long userId) throws SQLException { UsersManager usersManager = Context.getUsersManager(); - Set<Long> result = null; + Set<Long> result; if (Context.getPermissionsManager().getUserAdmin(getUserId())) { if (userId != 0) { result = usersManager.getUserItems(userId); diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java index 441898feb..77b42943b 100644 --- a/src/main/java/org/traccar/config/Keys.java +++ b/src/main/java/org/traccar/config/Keys.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -76,6 +76,12 @@ public final class Keys { "extra.handlers", String.class); /** + * Store original HEX or string data as "raw" attribute in the corresponding position. + */ + public static final ConfigKey DATABASE_SAVE_ORIGINAL = new ConfigKey( + "database.saveOriginal", Boolean.class); + + /** * Enable positions forwarding to other web server. */ public static final ConfigKey FORWARD_ENABLE = new ConfigKey( @@ -389,6 +395,24 @@ public final class Keys { "geolocation.processInvalidPositions", Boolean.class); /** + * Boolean flag to enable speed limit API to get speed limit values depending on location. Default value is false. + */ + public static final ConfigKey SPEED_LIMIT_ENABLE = new ConfigKey( + "speedLimit.enable", Boolean.class); + + /** + * Provider to use for speed limit. Available options: overpass. By default overpass is used. + */ + public static final ConfigKey SPEED_LIMIT_TYPE = new ConfigKey( + "speedLimit.type", String.class); + + /** + * Speed limit provider API URL address. + */ + public static final ConfigKey SPEED_LIMIT_URL = new ConfigKey( + "speedLimit.url", String.class); + + /** * Override latitude sign / hemisphere. Useful in cases where value is incorrect because of device bug. Value can be * N for North or S for South. */ @@ -423,6 +447,19 @@ public final class Keys { public static final ConfigKey WEB_REQUEST_LOG_RETAIN_DAYS = new ConfigKey( "web.requestLog.retainDays", Integer.class); + /** + * Disable systemd health checks. + */ + public static final ConfigKey WEB_DISABLE_HEALTH_CHECK = new ConfigKey( + "web.disableHealthCheck", Boolean.class); + + /** + * Sets SameSite cookie attribute value. + * Supported options: Lax, Strict, None. + */ + public static final ConfigKey WEB_SAME_SITE_COOKIE = new ConfigKey( + "web.sameSiteCookie", String.class); + private Keys() { } diff --git a/src/main/java/org/traccar/database/BaseObjectManager.java b/src/main/java/org/traccar/database/BaseObjectManager.java index d0024c005..be6310033 100644 --- a/src/main/java/org/traccar/database/BaseObjectManager.java +++ b/src/main/java/org/traccar/database/BaseObjectManager.java @@ -38,8 +38,8 @@ public class BaseObjectManager<T extends BaseModel> { private final DataManager dataManager; + private final Class<T> baseClass; private Map<Long, T> items; - private Class<T> baseClass; protected BaseObjectManager(DataManager dataManager, Class<T> baseClass) { this.dataManager = dataManager; diff --git a/src/main/java/org/traccar/database/CommandsManager.java b/src/main/java/org/traccar/database/CommandsManager.java index 485402807..843c89e82 100644 --- a/src/main/java/org/traccar/database/CommandsManager.java +++ b/src/main/java/org/traccar/database/CommandsManager.java @@ -41,7 +41,7 @@ public class CommandsManager extends ExtendedObjectManager<Command> { private final Map<Long, Queue<Command>> deviceQueues = new ConcurrentHashMap<>(); - private boolean queueing; + private final boolean queueing; public CommandsManager(DataManager dataManager, boolean queueing) { super(dataManager, Command.class); diff --git a/src/main/java/org/traccar/database/ConnectionManager.java b/src/main/java/org/traccar/database/ConnectionManager.java index 4d43bc71b..9342fd3de 100644 --- a/src/main/java/org/traccar/database/ConnectionManager.java +++ b/src/main/java/org/traccar/database/ConnectionManager.java @@ -17,7 +17,6 @@ package org.traccar.database; import io.netty.channel.Channel; import io.netty.util.Timeout; -import io.netty.util.TimerTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.Context; @@ -122,12 +121,9 @@ public class ConnectionManager { } if (status.equals(Device.STATUS_ONLINE)) { - timeouts.put(deviceId, GlobalTimer.getTimer().newTimeout(new TimerTask() { - @Override - public void run(Timeout timeout) { - if (!timeout.isCancelled()) { - updateDevice(deviceId, Device.STATUS_UNKNOWN, null); - } + timeouts.put(deviceId, GlobalTimer.getTimer().newTimeout(timeout1 -> { + if (!timeout1.isCancelled()) { + updateDevice(deviceId, Device.STATUS_UNKNOWN, null); } }, deviceTimeout, TimeUnit.MILLISECONDS)); } diff --git a/src/main/java/org/traccar/database/DataManager.java b/src/main/java/org/traccar/database/DataManager.java index 8e9071736..8b9690f77 100644 --- a/src/main/java/org/traccar/database/DataManager.java +++ b/src/main/java/org/traccar/database/DataManager.java @@ -79,7 +79,7 @@ public class DataManager { private boolean generateQueries; - private boolean forceLdap; + private final boolean forceLdap; public DataManager(Config config) throws Exception { this.config = config; diff --git a/src/main/java/org/traccar/database/DeviceManager.java b/src/main/java/org/traccar/database/DeviceManager.java index fe17f7ced..e621e6058 100644 --- a/src/main/java/org/traccar/database/DeviceManager.java +++ b/src/main/java/org/traccar/database/DeviceManager.java @@ -45,11 +45,11 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity private final Config config; private final long dataRefreshDelay; - private boolean lookupGroupsAttribute; + private final boolean lookupGroupsAttribute; private Map<String, Device> devicesByUniqueId; private Map<String, Device> devicesByPhone; - private AtomicLong devicesLastUpdate = new AtomicLong(); + private final AtomicLong devicesLastUpdate = new AtomicLong(); private final Map<Long, Position> positions = new ConcurrentHashMap<>(); diff --git a/src/main/java/org/traccar/database/PermissionsManager.java b/src/main/java/org/traccar/database/PermissionsManager.java index ced0df1c0..d2dc62394 100644 --- a/src/main/java/org/traccar/database/PermissionsManager.java +++ b/src/main/java/org/traccar/database/PermissionsManager.java @@ -196,7 +196,7 @@ public class PermissionsManager { public void checkDeviceLimit(long userId) throws SecurityException { int deviceLimit = getUser(userId).getDeviceLimit(); if (deviceLimit != -1) { - int deviceCount = 0; + int deviceCount; if (getUserManager(userId)) { deviceCount = Context.getDeviceManager().getAllManagedItems(userId).size(); } else { diff --git a/src/main/java/org/traccar/database/QueryBuilder.java b/src/main/java/org/traccar/database/QueryBuilder.java index 5528b2320..084d2940b 100644 --- a/src/main/java/org/traccar/database/QueryBuilder.java +++ b/src/main/java/org/traccar/database/QueryBuilder.java @@ -114,11 +114,7 @@ public final class QueryBuilder { name = name.toLowerCase(); // Add to list - List<Integer> indexList = paramMap.get(name); - if (indexList == null) { - indexList = new LinkedList<>(); - paramMap.put(name, indexList); - } + List<Integer> indexList = paramMap.computeIfAbsent(name, k -> new LinkedList<>()); indexList.add(index); index++; @@ -318,96 +314,72 @@ public final class QueryBuilder { final Class<?> parameterType, final Method method, final String name) { if (parameterType.equals(boolean.class)) { - processors.add(new ResultSetProcessor<T>() { - @Override - public void process(T object, ResultSet resultSet) throws SQLException { - try { - method.invoke(object, resultSet.getBoolean(name)); - } catch (IllegalAccessException | InvocationTargetException error) { - LOGGER.warn("Set property error", error); - } + processors.add((object, resultSet) -> { + try { + method.invoke(object, resultSet.getBoolean(name)); + } catch (IllegalAccessException | InvocationTargetException error) { + LOGGER.warn("Set property error", error); } }); } else if (parameterType.equals(int.class)) { - processors.add(new ResultSetProcessor<T>() { - @Override - public void process(T object, ResultSet resultSet) throws SQLException { - try { - method.invoke(object, resultSet.getInt(name)); - } catch (IllegalAccessException | InvocationTargetException error) { - LOGGER.warn("Set property error", error); - } + processors.add((object, resultSet) -> { + try { + method.invoke(object, resultSet.getInt(name)); + } catch (IllegalAccessException | InvocationTargetException error) { + LOGGER.warn("Set property error", error); } }); } else if (parameterType.equals(long.class)) { - processors.add(new ResultSetProcessor<T>() { - @Override - public void process(T object, ResultSet resultSet) throws SQLException { - try { - method.invoke(object, resultSet.getLong(name)); - } catch (IllegalAccessException | InvocationTargetException error) { - LOGGER.warn("Set property error", error); - } + processors.add((object, resultSet) -> { + try { + method.invoke(object, resultSet.getLong(name)); + } catch (IllegalAccessException | InvocationTargetException error) { + LOGGER.warn("Set property error", error); } }); } else if (parameterType.equals(double.class)) { - processors.add(new ResultSetProcessor<T>() { - @Override - public void process(T object, ResultSet resultSet) throws SQLException { - try { - method.invoke(object, resultSet.getDouble(name)); - } catch (IllegalAccessException | InvocationTargetException error) { - LOGGER.warn("Set property error", error); - } + processors.add((object, resultSet) -> { + try { + method.invoke(object, resultSet.getDouble(name)); + } catch (IllegalAccessException | InvocationTargetException error) { + LOGGER.warn("Set property error", error); } }); } else if (parameterType.equals(String.class)) { - processors.add(new ResultSetProcessor<T>() { - @Override - public void process(T object, ResultSet resultSet) throws SQLException { - try { - method.invoke(object, resultSet.getString(name)); - } catch (IllegalAccessException | InvocationTargetException error) { - LOGGER.warn("Set property error", error); - } + processors.add((object, resultSet) -> { + try { + method.invoke(object, resultSet.getString(name)); + } catch (IllegalAccessException | InvocationTargetException error) { + LOGGER.warn("Set property error", error); } }); } else if (parameterType.equals(Date.class)) { - processors.add(new ResultSetProcessor<T>() { - @Override - public void process(T object, ResultSet resultSet) throws SQLException { - try { - Timestamp timestamp = resultSet.getTimestamp(name); - if (timestamp != null) { - method.invoke(object, new Date(timestamp.getTime())); - } - } catch (IllegalAccessException | InvocationTargetException error) { - LOGGER.warn("Set property error", error); + processors.add((object, resultSet) -> { + try { + Timestamp timestamp = resultSet.getTimestamp(name); + if (timestamp != null) { + method.invoke(object, new Date(timestamp.getTime())); } + } catch (IllegalAccessException | InvocationTargetException error) { + LOGGER.warn("Set property error", error); } }); } else if (parameterType.equals(byte[].class)) { - processors.add(new ResultSetProcessor<T>() { - @Override - public void process(T object, ResultSet resultSet) throws SQLException { - try { - method.invoke(object, resultSet.getBytes(name)); - } catch (IllegalAccessException | InvocationTargetException error) { - LOGGER.warn("Set property error", error); - } + processors.add((object, resultSet) -> { + try { + method.invoke(object, resultSet.getBytes(name)); + } catch (IllegalAccessException | InvocationTargetException error) { + LOGGER.warn("Set property error", error); } }); } else { - processors.add(new ResultSetProcessor<T>() { - @Override - public void process(T object, ResultSet resultSet) throws SQLException { - String value = resultSet.getString(name); - if (value != null && !value.isEmpty()) { - try { - method.invoke(object, Context.getObjectMapper().readValue(value, parameterType)); - } catch (InvocationTargetException | IllegalAccessException | IOException error) { - LOGGER.warn("Set property error", error); - } + processors.add((object, resultSet) -> { + String value = resultSet.getString(name); + if (value != null && !value.isEmpty()) { + try { + method.invoke(object, Context.getObjectMapper().readValue(value, parameterType)); + } catch (InvocationTargetException | IllegalAccessException | IOException error) { + LOGGER.warn("Set property error", error); } } }); @@ -453,12 +425,12 @@ public final class QueryBuilder { while (resultSet.next()) { try { - T object = clazz.newInstance(); + T object = clazz.getDeclaredConstructor().newInstance(); for (ResultSetProcessor<T> processor : processors) { processor.process(object, resultSet); } result.add(object); - } catch (InstantiationException | IllegalAccessException e) { + } catch (ReflectiveOperationException e) { throw new IllegalArgumentException(); } } diff --git a/src/main/java/org/traccar/geofence/GeofenceCircle.java b/src/main/java/org/traccar/geofence/GeofenceCircle.java index f6fca63ca..eda63832d 100644 --- a/src/main/java/org/traccar/geofence/GeofenceCircle.java +++ b/src/main/java/org/traccar/geofence/GeofenceCircle.java @@ -50,7 +50,7 @@ public class GeofenceCircle extends GeofenceGeometry { @Override public String toWkt() { - String wkt = ""; + String wkt; wkt = "CIRCLE ("; wkt += String.valueOf(centerLatitude); wkt += " "; diff --git a/src/main/java/org/traccar/handler/SpeedLimitHandler.java b/src/main/java/org/traccar/handler/SpeedLimitHandler.java new file mode 100644 index 000000000..65f2c9cfe --- /dev/null +++ b/src/main/java/org/traccar/handler/SpeedLimitHandler.java @@ -0,0 +1,60 @@ +/* + * Copyright 2020 Anton Tananaev (anton@traccar.org) + * + * 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.handler; + +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.model.Position; +import org.traccar.speedlimit.SpeedLimitProvider; + +@ChannelHandler.Sharable +public class SpeedLimitHandler extends ChannelInboundHandlerAdapter { + + private static final Logger LOGGER = LoggerFactory.getLogger(SpeedLimitHandler.class); + + private final SpeedLimitProvider speedLimitProvider; + + public SpeedLimitHandler(SpeedLimitProvider speedLimitProvider) { + this.speedLimitProvider = speedLimitProvider; + } + + @Override + public void channelRead(final ChannelHandlerContext ctx, Object message) { + if (message instanceof Position) { + final Position position = (Position) message; + speedLimitProvider.getSpeedLimit(position.getLatitude(), position.getLongitude(), + new SpeedLimitProvider.SpeedLimitProviderCallback() { + @Override + public void onSuccess(double speedLimit) { + position.set(Position.KEY_SPEED_LIMIT, speedLimit); + ctx.fireChannelRead(position); + } + + @Override + public void onFailure(Throwable e) { + LOGGER.warn("Speed limit provider failed", e); + ctx.fireChannelRead(position); + } + }); + } else { + ctx.fireChannelRead(message); + } + } + +} diff --git a/src/main/java/org/traccar/handler/TimeHandler.java b/src/main/java/org/traccar/handler/TimeHandler.java index d8039518c..822c22a0a 100644 --- a/src/main/java/org/traccar/handler/TimeHandler.java +++ b/src/main/java/org/traccar/handler/TimeHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,8 +53,10 @@ public class TimeHandler extends ChannelInboundHandlerAdapter { Position position = (Position) msg; if (useServerTime) { position.setDeviceTime(position.getServerTime()); + position.setFixTime(position.getServerTime()); + } else { + position.setFixTime(position.getDeviceTime()); } - position.setFixTime(position.getDeviceTime()); } ctx.fireChannelRead(msg); diff --git a/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java b/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java index e534df9de..c396b28e9 100644 --- a/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java +++ b/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org) * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -122,6 +122,11 @@ public class OverspeedEventHandler extends BaseEventHandler { double speedLimit = deviceManager.lookupAttributeDouble(deviceId, ATTRIBUTE_SPEED_LIMIT, 0, true, false); + double positionSpeedLimit = position.getDouble(Position.KEY_SPEED_LIMIT); + if (positionSpeedLimit > 0) { + speedLimit = positionSpeedLimit; + } + double geofenceSpeedLimit = 0; long overspeedGeofenceId = 0; diff --git a/src/main/java/org/traccar/helper/BitBuffer.java b/src/main/java/org/traccar/helper/BitBuffer.java index f30a4557b..323807ba9 100644 --- a/src/main/java/org/traccar/helper/BitBuffer.java +++ b/src/main/java/org/traccar/helper/BitBuffer.java @@ -93,7 +93,7 @@ public class BitBuffer { return result; } else { result &= signBit - 1; - result += ~(signBit - 1); + result += -signBit; return result; } } diff --git a/src/main/java/org/traccar/helper/Checksum.java b/src/main/java/org/traccar/helper/Checksum.java index d41dc2992..8c3d0063a 100644 --- a/src/main/java/org/traccar/helper/Checksum.java +++ b/src/main/java/org/traccar/helper/Checksum.java @@ -26,12 +26,12 @@ public final class Checksum { public static class Algorithm { - private int poly; - private int init; - private boolean refIn; - private boolean refOut; - private int xorOut; - private int[] table; + private final int poly; + private final int init; + private final boolean refIn; + private final boolean refOut; + private final int xorOut; + private final int[] table; public Algorithm(int bits, int poly, int init, boolean refIn, boolean refOut, int xorOut) { this.poly = poly; @@ -151,13 +151,8 @@ public final class Checksum { return checksum; } - public static String nmea(String msg) { - int checksum = 0; - byte[] bytes = msg.getBytes(StandardCharsets.US_ASCII); - for (int i = 1; i < bytes.length; i++) { - checksum ^= bytes[i]; - } - return String.format("*%02x", checksum).toUpperCase(); + public static String nmea(String string) { + return String.format("*%02X", xor(string)); } public static int sum(ByteBuffer buf) { diff --git a/src/main/java/org/traccar/helper/DateBuilder.java b/src/main/java/org/traccar/helper/DateBuilder.java index 9752f6977..24b7d027a 100644 --- a/src/main/java/org/traccar/helper/DateBuilder.java +++ b/src/main/java/org/traccar/helper/DateBuilder.java @@ -21,7 +21,7 @@ import java.util.TimeZone; public class DateBuilder { - private Calendar calendar; + private final Calendar calendar; public DateBuilder() { this(TimeZone.getTimeZone("UTC")); diff --git a/src/main/java/org/traccar/helper/LogAction.java b/src/main/java/org/traccar/helper/LogAction.java index 16d55ec60..d16b25483 100644 --- a/src/main/java/org/traccar/helper/LogAction.java +++ b/src/main/java/org/traccar/helper/LogAction.java @@ -17,6 +17,10 @@ package org.traccar.helper; import java.beans.Introspector; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,6 +50,7 @@ public final class LogAction { private static final String PATTERN_LOGIN = "user: %d, action: %s"; private static final String PATTERN_LOGIN_FAILED = "login failed from: %s"; private static final String PATTERN_DEVICE_ACCUMULATORS = "user: %d, action: %s, deviceId: %d"; + private static final String PATTERN_REPORT = "user: %d, report: %s, from: %s, to: %s, devices: %s, groups: %s"; public static void create(long userId, BaseModel object) { logObjectAction(ACTION_CREATE, userId, object.getClass(), object.getId()); @@ -92,8 +97,8 @@ public final class LogAction { PATTERN_OBJECT, userId, action, Introspector.decapitalize(clazz.getSimpleName()), objectId)); } - private static void logLinkAction(String action, long userId, - Class<?> owner, long ownerId, Class<?> property, long propertyId) { + private static void logLinkAction( + String action, long userId, Class<?> owner, long ownerId, Class<?> property, long propertyId) { LOGGER.info(String.format( PATTERN_LINK, userId, action, Introspector.decapitalize(owner.getSimpleName()), ownerId, @@ -104,4 +109,13 @@ public final class LogAction { LOGGER.info(String.format(PATTERN_LOGIN, userId, action)); } + public static void logReport( + long userId, String report, Date from, Date to, List<Long> deviceIds, List<Long> groupIds) { + DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); + LOGGER.info(String.format( + PATTERN_REPORT, userId, report, + dateFormat.format(from), dateFormat.format(to), + deviceIds.toString(), groupIds.toString())); + } + } diff --git a/src/main/java/org/traccar/helper/ObdDecoder.java b/src/main/java/org/traccar/helper/ObdDecoder.java index 1bdcce352..b22065f4e 100644 --- a/src/main/java/org/traccar/helper/ObdDecoder.java +++ b/src/main/java/org/traccar/helper/ObdDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ public final class ObdDecoder { case MODE_FREEZE_FRAME: return decodeData( Integer.parseInt(value.substring(0, 2), 16), - Integer.parseInt(value.substring(2), 16), true); + Long.parseLong(value.substring(2), 16), true); case MODE_CODES: return decodeCodes(value); default: @@ -75,7 +75,7 @@ public final class ObdDecoder { } } - public static Map.Entry<String, Object> decodeData(int pid, int value, boolean convert) { + public static Map.Entry<String, Object> decodeData(int pid, long value, boolean convert) { switch (pid) { case 0x04: return createEntry(Position.KEY_ENGINE_LOAD, convert ? value * 100 / 255 : value); diff --git a/src/main/java/org/traccar/helper/Parser.java b/src/main/java/org/traccar/helper/Parser.java index 1471ec237..75106e2ba 100644 --- a/src/main/java/org/traccar/helper/Parser.java +++ b/src/main/java/org/traccar/helper/Parser.java @@ -242,7 +242,7 @@ public class Parser { public Date nextDateTime(DateTimeFormat format, String timeZone) { int year = 0, month = 0, day = 0; - int hour = 0, minute = 0, second = 0, millisecond = 0; + int hour, minute, second, millisecond = 0; switch (format) { case HMS: diff --git a/src/main/java/org/traccar/model/Command.java b/src/main/java/org/traccar/model/Command.java index abe538a10..099fb152d 100644 --- a/src/main/java/org/traccar/model/Command.java +++ b/src/main/java/org/traccar/model/Command.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,6 +31,7 @@ public class Command extends Message implements Cloneable { public static final String TYPE_ENGINE_RESUME = "engineResume"; public static final String TYPE_ALARM_ARM = "alarmArm"; public static final String TYPE_ALARM_DISARM = "alarmDisarm"; + public static final String TYPE_ALARM_DISMISS = "alarmDismiss"; public static final String TYPE_SET_TIMEZONE = "setTimezone"; public static final String TYPE_REQUEST_PHOTO = "requestPhoto"; public static final String TYPE_POWER_OFF = "powerOff"; diff --git a/src/main/java/org/traccar/model/Event.java b/src/main/java/org/traccar/model/Event.java index ee7fcc679..5eee2a0a0 100644 --- a/src/main/java/org/traccar/model/Event.java +++ b/src/main/java/org/traccar/model/Event.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,6 +40,7 @@ public class Event extends Message { public static final String TYPE_DEVICE_ONLINE = "deviceOnline"; public static final String TYPE_DEVICE_UNKNOWN = "deviceUnknown"; public static final String TYPE_DEVICE_OFFLINE = "deviceOffline"; + public static final String TYPE_DEVICE_INACTIVE = "deviceInactive"; public static final String TYPE_DEVICE_MOVING = "deviceMoving"; public static final String TYPE_DEVICE_STOPPED = "deviceStopped"; diff --git a/src/main/java/org/traccar/model/Position.java b/src/main/java/org/traccar/model/Position.java index 2c0e22c9e..6f70c8e21 100644 --- a/src/main/java/org/traccar/model/Position.java +++ b/src/main/java/org/traccar/model/Position.java @@ -85,6 +85,7 @@ public class Position extends Message { public static final String KEY_G_SENSOR = "gSensor"; public static final String KEY_ICCID = "iccid"; public static final String KEY_PHONE = "phone"; + public static final String KEY_SPEED_LIMIT = "speedLimit"; public static final String KEY_DTCS = "dtcs"; public static final String KEY_OBD_SPEED = "obdSpeed"; // knots diff --git a/src/main/java/org/traccar/model/Server.java b/src/main/java/org/traccar/model/Server.java index ad37e7078..112bfab26 100644 --- a/src/main/java/org/traccar/model/Server.java +++ b/src/main/java/org/traccar/model/Server.java @@ -166,4 +166,14 @@ public class Server extends ExtendedModel { public void setPoiLayer(String poiLayer) { this.poiLayer = poiLayer; } + + private String announcement; + + public String getAnnouncement() { + return announcement; + } + + public void setAnnouncement(String announcement) { + this.announcement = announcement; + } } diff --git a/src/main/java/org/traccar/model/Typed.java b/src/main/java/org/traccar/model/Typed.java index 313ec7bcd..fc671ac70 100644 --- a/src/main/java/org/traccar/model/Typed.java +++ b/src/main/java/org/traccar/model/Typed.java @@ -1,4 +1,5 @@ /* + * Copyright 2020 Anton Tananaev (anton@traccar.org) * Copyright 2016 Gabor Somogyi (gabor.g.somogyi@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,6 +16,8 @@ */ package org.traccar.model; +import java.util.Objects; + public class Typed { private String type; @@ -30,4 +33,20 @@ public class Typed { public void setType(String type) { this.type = type; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + return Objects.equals(type, ((Typed) o).type); + } + + @Override + public int hashCode() { + return Objects.hash(type); + } } diff --git a/src/main/java/org/traccar/notificators/Notificator.java b/src/main/java/org/traccar/notificators/Notificator.java index 5e40971c6..dd888bae9 100644 --- a/src/main/java/org/traccar/notificators/Notificator.java +++ b/src/main/java/org/traccar/notificators/Notificator.java @@ -27,13 +27,11 @@ public abstract class Notificator { private static final Logger LOGGER = LoggerFactory.getLogger(Notificator.class); public void sendAsync(final long userId, final Event event, final Position position) { - new Thread(new Runnable() { - public void run() { - try { - sendSync(userId, event, position); - } catch (MessageException | InterruptedException error) { - LOGGER.warn("Event send error", error); - } + new Thread(() -> { + try { + sendSync(userId, event, position); + } catch (MessageException | InterruptedException error) { + LOGGER.warn("Event send error", error); } }).start(); } diff --git a/src/main/java/org/traccar/protocol/AtrackFrameDecoder.java b/src/main/java/org/traccar/protocol/AtrackFrameDecoder.java index f071e2d97..8ed1fc8e8 100644 --- a/src/main/java/org/traccar/protocol/AtrackFrameDecoder.java +++ b/src/main/java/org/traccar/protocol/AtrackFrameDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,7 +39,7 @@ public class AtrackFrameDecoder extends BaseFrameDecoder { return buf.readRetainedSlice(KEEPALIVE_LENGTH); } - } else if (buf.getUnsignedShort(buf.readerIndex()) == 0x4050 && buf.getByte(buf.readerIndex() + 2) != ',') { + } else if (buf.getUnsignedByte(buf.readerIndex()) == 0x40 && buf.getByte(buf.readerIndex() + 2) != ',') { if (buf.readableBytes() > 6) { int length = buf.getUnsignedShort(buf.readerIndex() + 4) + 4 + 2; diff --git a/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java index 428b69cd9..56c00c7c1 100644 --- a/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 2020 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,10 +50,12 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder { private static final int MIN_DATA_LENGTH = 40; private boolean longDate; - private boolean decimalFuel; + private final boolean decimalFuel; private boolean custom; private String form; + private ByteBuf photo; + private final Map<Integer, String> alarmMap = new HashMap<>(); public AtrackProtocolDecoder(Protocol protocol) { @@ -510,20 +512,34 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder { return position; } - private List<Position> decodeBinary(Channel channel, SocketAddress remoteAddress, ByteBuf buf) { + private Position decodePhoto(DeviceSession deviceSession, ByteBuf buf, long id) { - buf.skipBytes(2); // prefix - buf.readUnsignedShort(); // checksum - buf.readUnsignedShort(); // length - int index = buf.readUnsignedShort(); + long time = buf.readUnsignedInt(); + int index = buf.readUnsignedByte(); + int count = buf.readUnsignedByte(); - long id = buf.readLong(); - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(id)); - if (deviceSession == null) { - return null; + if (photo == null) { + photo = Unpooled.buffer(); + } + photo.writeBytes(buf.readSlice(buf.readUnsignedShort())); + + if (index == count - 1) { + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + getLastLocation(position, new Date(time * 1000)); + + position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(String.valueOf(id), photo, "jpg")); + photo.release(); + photo = null; + + return position; } - sendResponse(channel, remoteAddress, id, index); + return null; + } + + private List<Position> decodeBinary(DeviceSession deviceSession, ByteBuf buf) { List<Position> positions = new LinkedList<>(); @@ -613,7 +629,26 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder { } else if (buf.getByte(buf.readerIndex() + 2) == ',') { return decodeText(channel, remoteAddress, buf.toString(StandardCharsets.US_ASCII).trim()); } else { - return decodeBinary(channel, remoteAddress, buf); + + String prefix = buf.readCharSequence(2, StandardCharsets.US_ASCII).toString(); + buf.readUnsignedShort(); // checksum + buf.readUnsignedShort(); // length + int index = buf.readUnsignedShort(); + + long id = buf.readLong(); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(id)); + if (deviceSession == null) { + return null; + } + + sendResponse(channel, remoteAddress, id, index); + + if (prefix.equals("@R")) { + return decodePhoto(deviceSession, buf, id); + } else { + return decodeBinary(deviceSession, buf); + } + } } diff --git a/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java b/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java index bc74b6576..09bd3572f 100644 --- a/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java @@ -73,7 +73,7 @@ public class CellocatorProtocolDecoder extends BaseProtocolDecoder { content.writeByte(packetNumber); content.writeZero(11); - ByteBuf reply = encodeContent(MSG_SERVER_ACKNOWLEDGE, (int) deviceId, packetNumber, content); + ByteBuf reply = encodeContent(MSG_SERVER_ACKNOWLEDGE, (int) deviceId, 0, content); channel.writeAndFlush(new NetworkMessage(reply, remoteAddress)); } } diff --git a/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java index d22106a80..115884e2c 100644 --- a/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java @@ -97,7 +97,7 @@ public class EasyTrackProtocolDecoder extends BaseProtocolDecoder { String sentence = (String) msg; String type = sentence.substring(20, 22); - if (type.equals("TX") && channel != null) { + if ((type.equals("TX") || type.equals("MQ")) && channel != null) { channel.writeAndFlush(new NetworkMessage(sentence + "#", remoteAddress)); } diff --git a/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java b/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java index c3fe7121e..613710587 100644 --- a/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2014 - 2020 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,8 @@ import org.traccar.model.Position; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; import java.util.Date; +import java.util.LinkedList; +import java.util.List; import java.util.regex.Pattern; public class EelinkProtocolDecoder extends BaseProtocolDecoder { @@ -76,6 +78,8 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder { case 0x08: case 0x09: return Position.ALARM_GPS_ANTENNA_CUT; + case 0x25: + return Position.ALARM_REMOVING; case 0x81: return Position.ALARM_LOW_SPEED; case 0x82: @@ -388,9 +392,29 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder { deviceSession = getDeviceSession(channel, remoteAddress); } + List<Position> positions = new LinkedList<>(); + + while (buf.isReadable()) { + Position position = decodePackage(channel, remoteAddress, buf, uniqueId, deviceSession); + if (position != null) { + positions.add(position); + } + } + + if (!positions.isEmpty()) { + return positions.size() > 1 ? positions : positions.iterator().next(); + } else { + return null; + } + } + + protected Position decodePackage( + Channel channel, SocketAddress remoteAddress, ByteBuf buf, + String uniqueId, DeviceSession deviceSession) throws Exception { + buf.skipBytes(2); // header int type = buf.readUnsignedByte(); - buf.readShort(); // length + buf = buf.readSlice(buf.readUnsignedShort()); int index = buf.readUnsignedShort(); if (type != MSG_GPS && type != MSG_DATA) { diff --git a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java index 9bc7cb504..fe42a44d7 100644 --- a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java @@ -60,7 +60,7 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder { .number("(-?d+),") // altitude .number("(d+),") // odometer .number("d+,") // runtime - .number("(x{4,8}),") // status + .number("(x+),") // status .number("(x+)?,") // input .number("(x+)?,") // output .number("(d+)|") // mcc diff --git a/src/main/java/org/traccar/protocol/FutureWayFrameDecoder.java b/src/main/java/org/traccar/protocol/FutureWayFrameDecoder.java new file mode 100644 index 000000000..812f78d02 --- /dev/null +++ b/src/main/java/org/traccar/protocol/FutureWayFrameDecoder.java @@ -0,0 +1,47 @@ +/* + * Copyright 2020 Anton Tananaev (anton@traccar.org) + * + * 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 io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import org.traccar.BaseFrameDecoder; +import org.traccar.helper.DataConverter; + +import java.nio.charset.StandardCharsets; + +public class FutureWayFrameDecoder extends BaseFrameDecoder { + + @Override + protected Object decode( + ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception { + + if (buf.readableBytes() < 10) { + return null; + } + + int length = Unpooled.wrappedBuffer(DataConverter.parseHex( + buf.getCharSequence(buf.readerIndex() + 2, 8, StandardCharsets.US_ASCII).toString())).readInt() + 17; + + if (buf.readableBytes() >= length) { + return buf.readRetainedSlice(length); + } + + return null; + } + +} diff --git a/src/main/java/org/traccar/protocol/FutureWayProtocol.java b/src/main/java/org/traccar/protocol/FutureWayProtocol.java new file mode 100644 index 000000000..73b53ee12 --- /dev/null +++ b/src/main/java/org/traccar/protocol/FutureWayProtocol.java @@ -0,0 +1,38 @@ +/* + * Copyright 2020 Anton Tananaev (anton@traccar.org) + * + * 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 io.netty.handler.codec.string.StringDecoder; +import io.netty.handler.codec.string.StringEncoder; +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; + +public class FutureWayProtocol extends BaseProtocol { + + public FutureWayProtocol() { + addServer(new TrackerServer(false, getName()) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline) { + pipeline.addLast(new FutureWayFrameDecoder()); + pipeline.addLast(new StringEncoder()); + pipeline.addLast(new StringDecoder()); + pipeline.addLast(new FutureWayProtocolDecoder(FutureWayProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/FutureWayProtocolDecoder.java b/src/main/java/org/traccar/protocol/FutureWayProtocolDecoder.java new file mode 100644 index 000000000..4b0f46e34 --- /dev/null +++ b/src/main/java/org/traccar/protocol/FutureWayProtocolDecoder.java @@ -0,0 +1,134 @@ +/* + * Copyright 2020 Anton Tananaev (anton@traccar.org) + * + * 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 io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.DeviceSession; +import org.traccar.Protocol; +import org.traccar.helper.DataConverter; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; +import org.traccar.model.CellTower; +import org.traccar.model.Network; +import org.traccar.model.Position; +import org.traccar.model.WifiAccessPoint; + +import java.net.SocketAddress; +import java.util.regex.Pattern; + +public class FutureWayProtocolDecoder extends BaseProtocolDecoder { + + public FutureWayProtocolDecoder(Protocol protocol) { + super(protocol); + } + + private static final Pattern PATTERN_GPS = new PatternBuilder() + .text("GPS:") + .expression("([AV]),") // validity + .number("(dd)(dd)(dd)") // date (yymmdd) + .number("(dd)(dd)(dd),") // time (hhmmss) + .number("(d+.d+)([NS]),") // latitude + .number("(d+.d+)([EW]),") // longitude + .number("(d+.d+),") // speed + .number("(d+.d+)") // course + .compile(); + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + String sentence = (String) msg; + + ByteBuf header = Unpooled.wrappedBuffer(DataConverter.parseHex(sentence.substring(0, 16))); + sentence = sentence.substring(16, sentence.length() - 4); + + header.readUnsignedByte(); // header + header.readUnsignedInt(); // length + int type = header.readUnsignedByte(); + header.readUnsignedShort(); // index + + if (type == 0x20) { + + getDeviceSession(channel, remoteAddress, sentence.split(",")[1].substring(5)); + + } else if (type == 0xA0) { + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + Network network = new Network(); + + for (String line : sentence.split("\r\n")) { + + if (line.startsWith("GPS")) { + + Parser parser = new Parser(PATTERN_GPS, line); + if (!parser.matches()) { + return null; + } + + position.setValid(parser.next().equals("A")); + position.setTime(parser.nextDateTime()); + position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM)); + position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM)); + position.setSpeed(parser.nextDouble()); + position.setCourse(parser.nextDouble()); + + } else if (line.startsWith("WIFI")) { + + for (String item : line.substring(line.indexOf(',') + 1).split("&")) { + String[] values = item.split("\\|"); + network.addWifiAccessPoint( + WifiAccessPoint.from(values[1].replace('-', ':'), Integer.parseInt(values[2]))); + } + + } else if (line.startsWith("LBS")) { + + String[] values = line.substring("LBS:".length()).split(","); + network.addCellTower(CellTower.from( + Integer.parseInt(values[0]), + Integer.parseInt(values[1]), + Integer.parseInt(values[3]), + Integer.parseInt(values[2]))); + + } + + } + + if (!network.getCellTowers().isEmpty() || !network.getWifiAccessPoints().isEmpty()) { + position.setNetwork(network); + } + + if (position.getFixTime() == null) { + getLastLocation(position, null); + } + + return position; + + } + + return null; + } + +} diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java index 283dbeb37..ec569b71b 100644 --- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,7 +45,7 @@ import java.util.regex.Pattern; public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { - private boolean ignoreFixTime; + private final boolean ignoreFixTime; public Gl200TextProtocolDecoder(Protocol protocol) { super(protocol); @@ -190,6 +190,12 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { .expression(PATTERN_LOCATION.pattern()) .expression(")+)") .groupBegin() + .number("d{1,2},,") + .number("(d{1,3}),") // battery + .number("[01],") // mode + .number("(?:[01])?,") // motion + .number("(?:-?d{1,2}.d)?,") // temperature + .or() .number("(d{1,7}.d)?,") // odometer .number("(d{5}:dd:dd)?,") // hour meter .number("(x+)?,") // adc 1 @@ -226,6 +232,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { .expression("((?:") .expression(PATTERN_LOCATION.pattern()) .expression(")+)") + .groupBegin() .number("(d{1,7}.d)?,") // odometer .number("(d{5}:dd:dd)?,") // hour meter .number("(x+)?,") // adc 1 @@ -233,6 +240,11 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { .number("(d{1,3})?,") // battery .number("(?:(xx)(xx)(xx))?,") // device status .expression("(.*)") // additional data + .or() + .number("d*,,") + .number("(d+),") // battery + .any() + .groupEnd() .number("(dddd)(dd)(dd)") // date (yyyymmdd) .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) .text(",") @@ -789,8 +801,14 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { } else if (BitUtil.check(ignition, 5)) { position.set(Position.KEY_IGNITION, true); } - position.set(Position.KEY_INPUT, parser.nextHexInt()); - position.set(Position.KEY_OUTPUT, parser.nextHexInt()); + int input = parser.nextHexInt(); + int output = parser.nextHexInt(); + position.set(Position.KEY_INPUT, input); + position.set(Position.PREFIX_IN + 1, BitUtil.check(input, 1)); + position.set(Position.PREFIX_IN + 2, BitUtil.check(input, 2)); + position.set(Position.KEY_OUTPUT, output); + position.set(Position.PREFIX_OUT + 1, BitUtil.check(output, 0)); + position.set(Position.PREFIX_OUT + 2, BitUtil.check(output, 1)); } } @@ -835,6 +853,10 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { } if (parser.hasNext()) { + position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt()); + } + + if (parser.hasNext()) { position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000); } position.set(Position.KEY_HOURS, parseHours(parser.next())); @@ -897,49 +919,58 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { if (power != null) { position.set(Position.KEY_POWER, power * 0.001); } - position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000); - position.set(Position.KEY_HOURS, parseHours(parser.next())); - position.set(Position.PREFIX_ADC + 1, parser.next()); - position.set(Position.PREFIX_ADC + 2, parser.next()); - position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt()); - decodeStatus(position, parser); + if (parser.hasNext(9)) { - int index = 0; - String[] data = parser.next().split(","); + position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000); + position.set(Position.KEY_HOURS, parseHours(parser.next())); + position.set(Position.PREFIX_ADC + 1, parser.next()); + position.set(Position.PREFIX_ADC + 2, parser.next()); + position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt()); - index += 1; // device type + decodeStatus(position, parser); - if (BitUtil.check(mask, 0)) { - index += 1; // digital fuel sensor data - } + int index = 0; + String[] data = parser.next().split(","); + + index += 1; // device type + + if (BitUtil.check(mask, 0)) { + index += 1; // digital fuel sensor data + } - if (BitUtil.check(mask, 1)) { - int deviceCount = Integer.parseInt(data[index++]); - for (int i = 1; i <= deviceCount; i++) { - index += 1; // id - index += 1; // type - if (!data[index++].isEmpty()) { - position.set(Position.PREFIX_TEMP + i, (short) Integer.parseInt(data[index - 1], 16) * 0.0625); + if (BitUtil.check(mask, 1)) { + int deviceCount = Integer.parseInt(data[index++]); + for (int i = 1; i <= deviceCount; i++) { + index += 1; // id + index += 1; // type + if (!data[index++].isEmpty()) { + position.set(Position.PREFIX_TEMP + i, (short) Integer.parseInt(data[index - 1], 16) * 0.0625); + } } } - } - if (BitUtil.check(mask, 2)) { - index += 1; // can data - } + if (BitUtil.check(mask, 2)) { + index += 1; // can data + } - if (BitUtil.check(mask, 3) || BitUtil.check(mask, 4)) { - int deviceCount = Integer.parseInt(data[index++]); - for (int i = 1; i <= deviceCount; i++) { - index += 1; // type - if (BitUtil.check(mask, 3)) { - position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(data[index++])); - } - if (BitUtil.check(mask, 4)) { - index += 1; // volume + if (BitUtil.check(mask, 3) || BitUtil.check(mask, 4)) { + int deviceCount = Integer.parseInt(data[index++]); + for (int i = 1; i <= deviceCount; i++) { + index += 1; // type + if (BitUtil.check(mask, 3)) { + position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(data[index++])); + } + if (BitUtil.check(mask, 4)) { + index += 1; // volume + } } } + + } + + if (parser.hasNext()) { + position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt()); } decodeDeviceTime(position, parser); diff --git a/src/main/java/org/traccar/protocol/GlobalSatProtocol.java b/src/main/java/org/traccar/protocol/GlobalSatProtocol.java index 7243be72a..e86b5dc30 100644 --- a/src/main/java/org/traccar/protocol/GlobalSatProtocol.java +++ b/src/main/java/org/traccar/protocol/GlobalSatProtocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,16 +21,22 @@ import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.model.Command; public class GlobalSatProtocol extends BaseProtocol { public GlobalSatProtocol() { + setSupportedDataCommands( + Command.TYPE_CUSTOM, + Command.TYPE_ALARM_DISMISS, + Command.TYPE_OUTPUT_CONTROL); addServer(new TrackerServer(false, getName()) { @Override protected void addProtocolHandlers(PipelineBuilder pipeline) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "!\r\n", "!")); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); + pipeline.addLast(new GlobalSatProtocolEncoder(GlobalSatProtocol.this)); pipeline.addLast(new GlobalSatProtocolDecoder(GlobalSatProtocol.this)); } }); diff --git a/src/main/java/org/traccar/protocol/GlobalSatProtocolEncoder.java b/src/main/java/org/traccar/protocol/GlobalSatProtocolEncoder.java new file mode 100644 index 000000000..4f56274da --- /dev/null +++ b/src/main/java/org/traccar/protocol/GlobalSatProtocolEncoder.java @@ -0,0 +1,58 @@ +/* + * Copyright 2020 Anton Tananaev (anton@traccar.org) + * + * 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.traccar.Protocol; +import org.traccar.StringProtocolEncoder; +import org.traccar.helper.Checksum; +import org.traccar.model.Command; + +public class GlobalSatProtocolEncoder extends StringProtocolEncoder { + + public GlobalSatProtocolEncoder(Protocol protocol) { + super(protocol); + } + + @Override + protected Object encodeCommand(Command command) { + + String formattedCommand = null; + + switch (command.getType()) { + case Command.TYPE_CUSTOM: + formattedCommand = formatCommand( + command, "GSC,%s,%s", Command.KEY_UNIQUE_ID, Command.KEY_DATA); + break; + case Command.TYPE_ALARM_DISMISS: + formattedCommand = formatCommand( + command, "GSC,%s,Na", Command.KEY_UNIQUE_ID); + break; + case Command.TYPE_OUTPUT_CONTROL: + formattedCommand = formatCommand( + command, "GSC,%s,Lo(%s,%s)", Command.KEY_UNIQUE_ID, Command.KEY_INDEX, Command.KEY_DATA); + break; + default: + break; + } + + if (formattedCommand != null) { + return formattedCommand + Checksum.nmea(formattedCommand) + '!'; + } else { + return null; + } + } + +} diff --git a/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java index de23ea170..b742d0cac 100644 --- a/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java @@ -60,9 +60,9 @@ import java.util.List; public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder { - private DocumentBuilder documentBuilder; - private XPath xPath; - private XPathExpression messageExpression; + private final DocumentBuilder documentBuilder; + private final XPath xPath; + private final XPathExpression messageExpression; public GlobalstarProtocolDecoder(Protocol protocol) { super(protocol); @@ -161,17 +161,20 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder { position.setLongitude(position.getLongitude() - 360); } - position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte())); + int speed = buf.readUnsignedByte(); + position.setSpeed(UnitsConverter.knotsFromKph(speed)); position.set("batteryReplace", BitUtil.check(buf.readUnsignedByte(), 7)); - positions.add(position); + if (speed != 0xff) { + positions.add(position); + } } } sendResponse(channel, document.getFirstChild().getAttributes().getNamedItem("messageID").getNodeValue()); - return positions; + return !positions.isEmpty() ? positions : null; } } diff --git a/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java index e00d83061..9b672cacc 100644 --- a/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java @@ -408,7 +408,7 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder { } } - if (sentence.substring(21, 21 + 2).equals("vr")) { + if (sentence.startsWith("vr", 21)) { return decodePhoto(channel, remoteAddress, sentence); } else if (sentence.substring(21, 21 + 3).contains("OBD")) { return decodeObd(channel, remoteAddress, sentence); diff --git a/src/main/java/org/traccar/protocol/GpsGateProtocolDecoder.java b/src/main/java/org/traccar/protocol/GpsGateProtocolDecoder.java index cc187225b..c158d3212 100644 --- a/src/main/java/org/traccar/protocol/GpsGateProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/GpsGateProtocolDecoder.java @@ -69,7 +69,8 @@ public class GpsGateProtocolDecoder extends BaseProtocolDecoder { private void send(Channel channel, SocketAddress remoteAddress, String message) { if (channel != null) { - channel.writeAndFlush(new NetworkMessage(message + Checksum.nmea(message) + "\r\n", remoteAddress)); + channel.writeAndFlush(new NetworkMessage( + message + Checksum.nmea(message.substring(1)) + "\r\n", remoteAddress)); } } diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java index 2c2ca7365..e003db51a 100644 --- a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java @@ -59,6 +59,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_LBS = 0x11; public static final int MSG_GPS_LBS_1 = 0x12; public static final int MSG_GPS_LBS_2 = 0x22; + public static final int MSG_GPS_LBS_3 = 0x37; public static final int MSG_STATUS = 0x13; public static final int MSG_SATELLITE = 0x14; public static final int MSG_STRING = 0x15; @@ -87,7 +88,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_COMMAND_2 = 0x82; public static final int MSG_TIME_REQUEST = 0x8A; public static final int MSG_INFO = 0x94; - public static final int MSG_RFID = 0x9B; + public static final int MSG_SERIAL = 0x9B; public static final int MSG_STRING_INFO = 0x21; public static final int MSG_GPS_2 = 0xA0; public static final int MSG_LBS_2 = 0xA1; @@ -114,6 +115,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { case MSG_GPS: case MSG_GPS_LBS_1: case MSG_GPS_LBS_2: + case MSG_GPS_LBS_3: case MSG_GPS_LBS_STATUS_1: case MSG_GPS_LBS_STATUS_2: case MSG_GPS_LBS_STATUS_3: @@ -134,6 +136,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { case MSG_LBS_STATUS: case MSG_GPS_LBS_1: case MSG_GPS_LBS_2: + case MSG_GPS_LBS_3: case MSG_GPS_LBS_STATUS_1: case MSG_GPS_LBS_STATUS_2: case MSG_GPS_LBS_STATUS_3: @@ -736,57 +739,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { } else if (isSupported(type)) { - if (hasGps(type)) { - decodeGps(position, buf, false, deviceSession.getTimeZone()); - } else { - getLastLocation(position, null); - } - - if (hasLbs(type)) { - decodeLbs(position, buf, hasStatus(type)); - } - - if (hasStatus(type)) { - decodeStatus(position, buf); - } - - if (type == MSG_GPS_LBS_1 && buf.readableBytes() > 75 + 6) { - position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); - String data = buf.readCharSequence(buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString(); - buf.readUnsignedByte(); // alarm - buf.readUnsignedByte(); // swiped - position.set("driverLicense", data.trim()); - } - - if (type == MSG_GPS_LBS_1 && buf.readableBytes() == 2 + 6) { - int mask = buf.readUnsignedShort(); - position.set(Position.KEY_IGNITION, BitUtil.check(mask, 8 + 7)); - position.set(Position.PREFIX_IN + 2, BitUtil.check(mask, 8 + 6)); - if (BitUtil.check(mask, 8 + 4)) { - int value = BitUtil.to(mask, 8 + 1); - if (BitUtil.check(mask, 8 + 1)) { - value = -value; - } - position.set(Position.PREFIX_TEMP + 1, value); - } else { - int value = BitUtil.to(mask, 8 + 2); - if (BitUtil.check(mask, 8 + 5)) { - position.set(Position.PREFIX_ADC + 1, value); - } else { - position.set(Position.PREFIX_ADC + 1, value * 0.1); - } - } - } - - if (type == MSG_GPS_LBS_2 && buf.readableBytes() == 3 + 6) { - position.set(Position.KEY_IGNITION, buf.readUnsignedByte() > 0); - position.set(Position.KEY_EVENT, buf.readUnsignedByte()); // reason - position.set(Position.KEY_ARCHIVE, buf.readUnsignedByte() > 0); - } - - if (buf.readableBytes() == 4 + 6) { - position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); - } + decodeBasicUniversal(buf, deviceSession, type, position); } else if (type == MSG_ALARM) { boolean extendedAlarm = dataLength > 7; @@ -850,6 +803,80 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { return position; } + private void decodeBasicUniversal(ByteBuf buf, DeviceSession deviceSession, int type, Position position) { + + if (hasGps(type)) { + decodeGps(position, buf, false, deviceSession.getTimeZone()); + } else { + getLastLocation(position, null); + } + + if (hasLbs(type)) { + decodeLbs(position, buf, hasStatus(type)); + } + + if (hasStatus(type)) { + decodeStatus(position, buf); + } + + if (type == MSG_GPS_LBS_1 && buf.readableBytes() > 75 + 6) { + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); + String data = buf.readCharSequence(buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString(); + buf.readUnsignedByte(); // alarm + buf.readUnsignedByte(); // swiped + position.set("driverLicense", data.trim()); + } + + if (type == MSG_GPS_LBS_1 && buf.readableBytes() == 2 + 6) { + int mask = buf.readUnsignedShort(); + position.set(Position.KEY_IGNITION, BitUtil.check(mask, 8 + 7)); + position.set(Position.PREFIX_IN + 2, BitUtil.check(mask, 8 + 6)); + if (BitUtil.check(mask, 8 + 4)) { + int value = BitUtil.to(mask, 8 + 1); + if (BitUtil.check(mask, 8 + 1)) { + value = -value; + } + position.set(Position.PREFIX_TEMP + 1, value); + } else { + int value = BitUtil.to(mask, 8 + 2); + if (BitUtil.check(mask, 8 + 5)) { + position.set(Position.PREFIX_ADC + 1, value); + } else { + position.set(Position.PREFIX_ADC + 1, value * 0.1); + } + } + } + + if ((type == MSG_GPS_LBS_2 || type == MSG_GPS_LBS_3) && buf.readableBytes() >= 3 + 6) { + position.set(Position.KEY_IGNITION, buf.readUnsignedByte() > 0); + position.set(Position.KEY_EVENT, buf.readUnsignedByte()); // reason + position.set(Position.KEY_ARCHIVE, buf.readUnsignedByte() > 0); + } + + if (type == MSG_GPS_LBS_3) { + int module = buf.readUnsignedShort(); + int length = buf.readUnsignedByte(); + switch (module) { + case 0x0027: + position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01); + break; + case 0x002E: + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); + break; + case 0x003B: + position.setAccuracy(buf.readUnsignedShort() * 0.01); + break; + default: + buf.skipBytes(length); + break; + } + } + + if (buf.readableBytes() == 4 + 6) { + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); + } + } + private Object decodeExtended(Channel channel, SocketAddress remoteAddress, ByteBuf buf) { DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); @@ -1026,18 +1053,6 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { return position; - } else if (type == MSG_RFID) { - - getLastLocation(position, null); - - buf.readUnsignedByte(); // external device type code - buf.readUnsignedByte(); // card type - position.set( - Position.KEY_DRIVER_UNIQUE_ID, - buf.readCharSequence(buf.readableBytes() - 9, StandardCharsets.US_ASCII).toString()); - - return position; - } else if (type == MSG_GPS_MODULAR) { return decodeExtendedModular(buf, deviceSession); @@ -1205,6 +1220,27 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { return position; + } else if (type == MSG_SERIAL) { + + position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + getLastLocation(position, null); + + buf.readUnsignedByte(); // external device type code + int length = buf.readableBytes() - 9; // line break + checksum + index + checksum + footer + if (length < 8) { + position.set( + Position.PREFIX_TEMP + 1, + Double.parseDouble(buf.readCharSequence(length - 1, StandardCharsets.US_ASCII).toString())); + } else { + buf.readUnsignedByte(); // card type + position.set( + Position.KEY_DRIVER_UNIQUE_ID, + buf.readCharSequence(length - 1, StandardCharsets.US_ASCII).toString()); + } + + return position; + } return null; diff --git a/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java index 8d39abead..f1d146bda 100644 --- a/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java @@ -190,6 +190,9 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder { position.set( Position.KEY_VIN, buf.readCharSequence(length, StandardCharsets.US_ASCII).toString()); break; + case 0x0011: + position.set(Position.KEY_HOURS, buf.readUnsignedInt() * 0.05); + break; case 0x0020: String[] cells = buf.readCharSequence( length, StandardCharsets.US_ASCII).toString().split("\\+"); diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java index 841e2235d..b9b156f93 100644 --- a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java @@ -57,13 +57,17 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { public static final int RESULT_SUCCESS = 0; - public static ByteBuf formatMessage(int type, ByteBuf id, ByteBuf data) { + public static ByteBuf formatMessage(int type, ByteBuf id, boolean shortIndex, ByteBuf data) { ByteBuf buf = Unpooled.buffer(); buf.writeByte(0x7e); buf.writeShort(type); buf.writeShort(data.readableBytes()); buf.writeBytes(id); - buf.writeShort(0); // index + if (shortIndex) { + buf.writeByte(1); + } else { + buf.writeShort(1); + } buf.writeBytes(data); data.release(); buf.writeByte(Checksum.xor(buf.nioBuffer(1, buf.readableBytes() - 1))); @@ -79,18 +83,18 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { response.writeShort(type); response.writeByte(RESULT_SUCCESS); channel.writeAndFlush(new NetworkMessage( - formatMessage(MSG_GENERAL_RESPONSE, id, response), remoteAddress)); + formatMessage(MSG_GENERAL_RESPONSE, id, false, response), remoteAddress)); } } private void sendGeneralResponse2( - Channel channel, SocketAddress remoteAddress, ByteBuf id, int index) { + Channel channel, SocketAddress remoteAddress, ByteBuf id, int type) { if (channel != null) { ByteBuf response = Unpooled.buffer(); - response.writeShort(index); + response.writeShort(type); response.writeByte(RESULT_SUCCESS); channel.writeAndFlush(new NetworkMessage( - formatMessage(MSG_GENERAL_RESPONSE_2, id, response), remoteAddress)); + formatMessage(MSG_GENERAL_RESPONSE_2, id, true, response), remoteAddress)); } } @@ -161,7 +165,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { response.writeByte(RESULT_SUCCESS); response.writeBytes(ByteBufUtil.hexDump(id).getBytes(StandardCharsets.US_ASCII)); channel.writeAndFlush(new NetworkMessage( - formatMessage(MSG_TERMINAL_REGISTER_RESPONSE, id, response), remoteAddress)); + formatMessage(MSG_TERMINAL_REGISTER_RESPONSE, id, false, response), remoteAddress)); } } else if (type == MSG_TERMINAL_AUTH) { @@ -170,12 +174,14 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { } else if (type == MSG_LOCATION_REPORT) { + sendGeneralResponse(channel, remoteAddress, id, type, index); + return decodeLocation(deviceSession, buf); } else if (type == MSG_LOCATION_REPORT_2 || type == MSG_LOCATION_REPORT_BLIND) { if (BitUtil.check(attribute, 15)) { - sendGeneralResponse2(channel, remoteAddress, id, index); + sendGeneralResponse2(channel, remoteAddress, id, type); } return decodeLocation2(deviceSession, buf, type); @@ -296,11 +302,24 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { case 0xD3: position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.1); break; + case 0xD4: + position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); + break; + case 0xD5: + position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01); + break; + case 0xDA: + buf.readUnsignedShort(); // string cut count + int deviceStatus = buf.readUnsignedByte(); + position.set("string", BitUtil.check(deviceStatus, 0)); + position.set(Position.KEY_MOTION, BitUtil.check(deviceStatus, 2)); + position.set("cover", BitUtil.check(deviceStatus, 3)); + break; case 0xEB: while (buf.readerIndex() < endIndex) { - int tenetLength = buf.readUnsignedShort(); - int tenetType = buf.readUnsignedShort(); - switch (tenetType) { + int extendedLength = buf.readUnsignedShort(); + int extendedType = buf.readUnsignedShort(); + switch (extendedType) { case 0x0001: position.set("fuel1", buf.readUnsignedShort() * 0.1); buf.readUnsignedByte(); // unused @@ -309,8 +328,11 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { position.set("fuel2", Double.parseDouble( buf.readCharSequence(6, StandardCharsets.US_ASCII).toString())); break; + case 0x00CE: + position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01); + break; default: - buf.skipBytes(tenetLength - 2); + buf.skipBytes(extendedLength - 2); break; } } diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java index 40d07230d..55c1e0c3b 100644 --- a/src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java +++ b/src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,22 +50,22 @@ public class HuabaoProtocolEncoder extends BaseProtocolEncoder { data.writeByte(0x01); data.writeBytes(time); return HuabaoProtocolDecoder.formatMessage( - HuabaoProtocolDecoder.MSG_OIL_CONTROL, id, data); + HuabaoProtocolDecoder.MSG_OIL_CONTROL, id, false, data); } else { data.writeByte(0xf0); return HuabaoProtocolDecoder.formatMessage( - HuabaoProtocolDecoder.MSG_TERMINAL_CONTROL, id, data); + HuabaoProtocolDecoder.MSG_TERMINAL_CONTROL, id, false, data); } case Command.TYPE_ENGINE_RESUME: if (alternative) { data.writeByte(0x00); data.writeBytes(time); return HuabaoProtocolDecoder.formatMessage( - HuabaoProtocolDecoder.MSG_OIL_CONTROL, id, data); + HuabaoProtocolDecoder.MSG_OIL_CONTROL, id, false, data); } else { data.writeByte(0xf1); return HuabaoProtocolDecoder.formatMessage( - HuabaoProtocolDecoder.MSG_TERMINAL_CONTROL, id, data); + HuabaoProtocolDecoder.MSG_TERMINAL_CONTROL, id, false, data); } default: return null; diff --git a/src/main/java/org/traccar/protocol/IntellitracProtocolDecoder.java b/src/main/java/org/traccar/protocol/IntellitracProtocolDecoder.java index 897606270..930d4f23b 100644 --- a/src/main/java/org/traccar/protocol/IntellitracProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/IntellitracProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 2020 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,7 +44,7 @@ public class IntellitracProtocolDecoder extends BaseProtocolDecoder { .number("(d+.?d*),") // course .number("(-?d+.?d*),") // altitude .number("(d+),") // satellites - .number("(d+),") // index + .number("(d+),") // event .number("(d+),") // input .number("(d+),?") // output .number("(d+.d+)?,?") // adc1 @@ -65,6 +65,30 @@ public class IntellitracProtocolDecoder extends BaseProtocolDecoder { .any() .compile(); + private String decodeAlarm(int value) { + switch (value) { + case 164: + return Position.ALARM_GEOFENCE_ENTER; + case 165: + return Position.ALARM_GEOFENCE_EXIT; + case 168: + case 169: + return Position.ALARM_LOW_POWER; + case 170: + return Position.ALARM_POWER_OFF; + case 176: + return Position.ALARM_POWER_RESTORED; + case 180: + return Position.ALARM_FALL_DOWN; + case 225: + return Position.ALARM_JAMMING; + case 995: + return Position.ALARM_SOS; + default: + return null; + } + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { @@ -74,12 +98,12 @@ public class IntellitracProtocolDecoder extends BaseProtocolDecoder { return null; } - Position position = new Position(getProtocolName()); - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); if (deviceSession == null) { return null; } + + Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); position.setTime(parser.nextDateTime()); @@ -92,7 +116,11 @@ public class IntellitracProtocolDecoder extends BaseProtocolDecoder { position.setAltitude(parser.nextDouble()); position.set(Position.KEY_SATELLITES, parser.nextInt()); - position.set(Position.KEY_INDEX, parser.nextLong()); + + int event = parser.nextInt(); + position.set(Position.KEY_ALARM, decodeAlarm(event)); + position.set(Position.KEY_EVENT, event); + position.set(Position.KEY_INPUT, parser.nextInt()); position.set(Position.KEY_OUTPUT, parser.nextInt()); @@ -100,16 +128,18 @@ public class IntellitracProtocolDecoder extends BaseProtocolDecoder { position.set(Position.PREFIX_ADC + 2, parser.nextDouble()); // J1939 data - position.set(Position.KEY_OBD_SPEED, parser.nextInt()); - position.set(Position.KEY_RPM, parser.nextInt()); - position.set("coolant", parser.nextInt()); - position.set(Position.KEY_FUEL_LEVEL, parser.nextInt()); - position.set(Position.KEY_FUEL_CONSUMPTION, parser.nextInt()); - position.set(Position.PREFIX_TEMP + 1, parser.nextInt()); - position.set("chargerPressure", parser.nextInt()); - position.set("tpl", parser.nextInt()); - position.set(Position.KEY_AXLE_WEIGHT, parser.nextInt()); - position.set(Position.KEY_OBD_ODOMETER, parser.nextInt()); + if (parser.hasNext(10)) { + position.set(Position.KEY_OBD_SPEED, parser.nextInt()); + position.set(Position.KEY_RPM, parser.nextInt()); + position.set("coolant", parser.nextInt()); + position.set(Position.KEY_FUEL_LEVEL, parser.nextInt()); + position.set(Position.KEY_FUEL_CONSUMPTION, parser.nextInt()); + position.set(Position.PREFIX_TEMP + 1, parser.nextInt()); + position.set("chargerPressure", parser.nextInt()); + position.set("tpl", parser.nextInt()); + position.set(Position.KEY_AXLE_WEIGHT, parser.nextInt()); + position.set(Position.KEY_OBD_ODOMETER, parser.nextInt()); + } return position; } diff --git a/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java b/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java index f66669a98..94c9a3038 100644 --- a/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java @@ -90,6 +90,10 @@ public class ItsProtocolDecoder extends BaseProtocolDecoder { .number("(-?d+),") // tilt x .or() .number("d+,") // index + .number("(d+.?d*),") // odometer + .number("x+,") // checksum + .or() + .number("d+,") // index .number("(d+.?d*),") // adc1 .number("(d+.?d*),") // adc2 .groupEnd("?") @@ -244,6 +248,10 @@ public class ItsProtocolDecoder extends BaseProtocolDecoder { position.set("tiltX", parser.nextInt()); } + if (parser.hasNext()) { + position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000); + } + if (parser.hasNext(2)) { position.set(Position.PREFIX_ADC + 1, parser.nextDouble()); position.set(Position.PREFIX_ADC + 2, parser.nextDouble()); diff --git a/src/main/java/org/traccar/protocol/L100ProtocolDecoder.java b/src/main/java/org/traccar/protocol/L100ProtocolDecoder.java index 9868de435..5b5eb7d60 100644 --- a/src/main/java/org/traccar/protocol/L100ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/L100ProtocolDecoder.java @@ -138,7 +138,7 @@ public class L100ProtocolDecoder extends BaseProtocolDecoder { String sentence = (String) msg; if (sentence.startsWith("L") || sentence.startsWith("H")) { - if (sentence.substring(2, 8).equals("ATLOBD")) { + if (sentence.startsWith("ATLOBD", 2)) { return decodeObdData(channel, remoteAddress, sentence); } else { return decodeObdLocation(channel, remoteAddress, sentence); diff --git a/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java b/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java index 0c72568f3..4abb75025 100644 --- a/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java @@ -152,7 +152,7 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder { if (responseCode != null) { String response = "$AVCFG," + devicePassword + "," + responseCode; - response += Checksum.nmea(response) + "\r\n"; + response += Checksum.nmea(response.substring(1)) + "\r\n"; channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); } @@ -163,7 +163,7 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder { if (Character.isLowerCase(status.charAt(0))) { String response = "$EAVACK," + event + "," + checksum; - response += Checksum.nmea(response) + "\r\n"; + response += Checksum.nmea(response.substring(1)) + "\r\n"; channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); } diff --git a/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java b/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java index 0c9f8ebb8..aaa5a70f7 100644 --- a/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java +++ b/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java @@ -29,7 +29,7 @@ public class LaipacProtocolEncoder extends StringProtocolEncoder { @Override protected String formatCommand(Command command, String format, String... keys) { String sentence = super.formatCommand(command, "$" + format, keys); - sentence += Checksum.nmea(sentence) + "\r\n"; + sentence += Checksum.nmea(sentence.substring(1)) + "\r\n"; return sentence; } diff --git a/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java index 529496928..c43f1ea83 100644 --- a/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java @@ -177,8 +177,8 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { position.setNetwork(new Network(CellTower.from( parser.nextInt(), parser.nextInt(), parser.nextHexInt(), parser.nextHexInt(), rssi))); - position.set(Position.KEY_OUTPUT, parser.nextHexInt()); position.set(Position.KEY_INPUT, parser.nextHexInt()); + position.set(Position.KEY_OUTPUT, parser.nextHexInt()); for (int i = 1; i <= 3; i++) { position.set(Position.PREFIX_ADC + i, parser.nextHexInt()); @@ -421,6 +421,22 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { case 0x1A: position.set(Position.KEY_POWER, buf.readUnsignedShortLE() * 0.01); break; + case 0x91: + case 0x92: + position.set(Position.KEY_OBD_SPEED, buf.readUnsignedShortLE()); + break; + case 0x98: + position.set(Position.KEY_FUEL_USED, buf.readUnsignedShortLE()); + break; + case 0x99: + position.set(Position.KEY_RPM, buf.readUnsignedShortLE()); + break; + case 0x9C: + position.set(Position.KEY_COOLANT_TEMP, buf.readUnsignedShortLE()); + break; + case 0xC9: + position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedShortLE()); + break; default: buf.readUnsignedShortLE(); break; @@ -440,9 +456,18 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { case 0x04: position.setTime(new Date((946684800 + buf.readUnsignedIntLE()) * 1000)); // 2000-01-01 break; + case 0x0C: + position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE()); + break; case 0x0D: position.set("runtime", buf.readUnsignedIntLE()); break; + case 0xA0: + position.set(Position.KEY_FUEL_USED, buf.readUnsignedIntLE() * 0.001); + break; + case 0xA2: + position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedIntLE() * 0.01); + break; default: buf.readUnsignedIntLE(); break; diff --git a/src/main/java/org/traccar/protocol/MictrackProtocol.java b/src/main/java/org/traccar/protocol/MictrackProtocol.java index c8d64fd81..9fd9666e4 100644 --- a/src/main/java/org/traccar/protocol/MictrackProtocol.java +++ b/src/main/java/org/traccar/protocol/MictrackProtocol.java @@ -15,7 +15,6 @@ */ package org.traccar.protocol; -import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; @@ -28,7 +27,6 @@ public class MictrackProtocol extends BaseProtocol { addServer(new TrackerServer(false, getName()) { @Override protected void addProtocolHandlers(PipelineBuilder pipeline) { - pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new MictrackProtocolDecoder(MictrackProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java index 0f815ca7b..5ea9f148c 100644 --- a/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java @@ -66,6 +66,8 @@ public class MictrackProtocolDecoder extends BaseProtocolDecoder { private String decodeAlarm(int event) { switch (event) { + case 0: + return Position.ALARM_POWER_ON; case 5: return Position.ALARM_SOS; case 8: @@ -209,8 +211,12 @@ public class MictrackProtocolDecoder extends BaseProtocolDecoder { } private Object decodeLowAltitude( - Channel channel, SocketAddress remoteAddress, String sentence) throws Exception { - String deviceId = sentence.substring(0, sentence.indexOf("$")); + Channel channel, SocketAddress remoteAddress, String sentence) { + int separator = sentence.indexOf("$"); + if (separator < 0) { + return null; + } + String deviceId = sentence.substring(0, separator); DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, deviceId); if (deviceSession == null) { diff --git a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java index b8ab134c5..5b04992ec 100644 --- a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,6 +34,10 @@ import org.traccar.model.WifiAccessPoint; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; import java.util.Date; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { @@ -100,18 +104,33 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { if (type == MSG_DATA) { + List<Position> positions = new LinkedList<>(); + Set<Integer> keys = new HashSet<>(); + boolean hasLocation = false; Position position = new Position(getProtocolName()); + DeviceSession deviceSession = null; + while (buf.isReadable()) { int endIndex = buf.readUnsignedByte() + buf.readerIndex(); int key = buf.readUnsignedByte(); + + if (keys.contains(key)) { + if (!hasLocation) { + getLastLocation(position, null); + } + positions.add(position); + keys.clear(); + hasLocation = false; + position = new Position(getProtocolName()); + } + keys.add(key); + switch (key) { case 0x01: - DeviceSession deviceSession = getDeviceSession( + deviceSession = getDeviceSession( channel, remoteAddress, buf.readCharSequence(15, StandardCharsets.US_ASCII).toString()); - if (deviceSession == null) { - return null; - } + position.setDeviceId(deviceSession.getDeviceId()); break; case 0x02: @@ -122,6 +141,7 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.001); break; case 0x20: + hasLocation = true; position.setLatitude(buf.readIntLE() * 0.0000001); position.setLongitude(buf.readIntLE() * 0.0000001); position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE())); @@ -169,6 +189,10 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_BATTERY_LEVEL, BitUtil.from(status, 24)); position.set(Position.KEY_STATUS, status); break; + case 0x30: + buf.readUnsignedInt(); // timestamp + position.set(Position.KEY_STEPS, buf.readUnsignedInt()); + break; case 0x40: buf.readUnsignedIntLE(); // timestamp int heartRate = buf.readUnsignedByte(); @@ -182,11 +206,20 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { buf.readerIndex(endIndex); } - if (!position.getAttributes().containsKey(Position.KEY_SATELLITES)) { + if (!hasLocation) { getLastLocation(position, null); } + positions.add(position); + + if (deviceSession != null) { + for (Position p : positions) { + p.setDeviceId(deviceSession.getDeviceId()); + } + } else { + return null; + } - return position.getDeviceId() > 0 ? position : null; + return positions; } diff --git a/src/main/java/org/traccar/protocol/MoovboxProtocol.java b/src/main/java/org/traccar/protocol/MoovboxProtocol.java new file mode 100644 index 000000000..7b554266f --- /dev/null +++ b/src/main/java/org/traccar/protocol/MoovboxProtocol.java @@ -0,0 +1,39 @@ +/* + * Copyright 2020 Anton Tananaev (anton@traccar.org) + * + * 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 io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpRequestDecoder; +import io.netty.handler.codec.http.HttpResponseEncoder; +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; + +public class MoovboxProtocol extends BaseProtocol { + + public MoovboxProtocol() { + addServer(new TrackerServer(false, getName()) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline) { + pipeline.addLast(new HttpResponseEncoder()); + pipeline.addLast(new HttpRequestDecoder()); + pipeline.addLast(new HttpObjectAggregator(65535)); + pipeline.addLast(new MoovboxProtocolDecoder(MoovboxProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/MoovboxProtocolDecoder.java b/src/main/java/org/traccar/protocol/MoovboxProtocolDecoder.java new file mode 100644 index 000000000..3116d073c --- /dev/null +++ b/src/main/java/org/traccar/protocol/MoovboxProtocolDecoder.java @@ -0,0 +1,106 @@ +/* + * Copyright 2020 Anton Tananaev (anton@traccar.org) + * + * 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 com.fasterxml.jackson.databind.util.ByteBufferBackedInputStream; +import io.netty.channel.Channel; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpResponseStatus; +import org.traccar.BaseHttpProtocolDecoder; +import org.traccar.DeviceSession; +import org.traccar.Protocol; +import org.traccar.model.Position; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; +import java.net.SocketAddress; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +public class MoovboxProtocolDecoder extends BaseHttpProtocolDecoder { + + private final DocumentBuilder documentBuilder; + private final XPath xPath; + private final XPathExpression messageExpression; + + public MoovboxProtocolDecoder(Protocol protocol) { + super(protocol); + try { + DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); + builderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + builderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false); + builderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + builderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + builderFactory.setXIncludeAware(false); + builderFactory.setExpandEntityReferences(false); + documentBuilder = builderFactory.newDocumentBuilder(); + xPath = XPathFactory.newInstance().newXPath(); + messageExpression = xPath.compile("//gps/coordinates/coordinate"); + } catch (ParserConfigurationException | XPathExpressionException e) { + throw new RuntimeException(e); + } + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + FullHttpRequest request = (FullHttpRequest) msg; + + Document document = documentBuilder.parse(new ByteBufferBackedInputStream(request.content().nioBuffer())); + + String id = document.getDocumentElement().getAttribute("id"); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id); + if (deviceSession == null) { + return null; + } + + NodeList nodes = (NodeList) messageExpression.evaluate(document, XPathConstants.NODESET); + List<Position> positions = new LinkedList<>(); + + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.setValid(true); + position.setTime(new Date(Long.parseLong(xPath.evaluate("time", node)) * 1000)); + position.setLatitude(Double.parseDouble(xPath.evaluate("longitude", node))); + position.setLongitude(Double.parseDouble(xPath.evaluate("latitude", node))); + position.setAltitude(Double.parseDouble(xPath.evaluate("altitude", node))); + position.setSpeed(Double.parseDouble(xPath.evaluate("speed", node))); + + position.set(Position.KEY_SATELLITES, Integer.parseInt(xPath.evaluate("satellites", node))); + + positions.add(position); + } + + sendResponse(channel, HttpResponseStatus.OK); + return positions; + } + +} diff --git a/src/main/java/org/traccar/protocol/NetProtocol.java b/src/main/java/org/traccar/protocol/NetProtocol.java new file mode 100644 index 000000000..c114d19fc --- /dev/null +++ b/src/main/java/org/traccar/protocol/NetProtocol.java @@ -0,0 +1,39 @@ +/* + * Copyright 2020 Anton Tananaev (anton@traccar.org) + * + * 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 io.netty.handler.codec.string.StringDecoder; +import io.netty.handler.codec.string.StringEncoder; +import org.traccar.BaseProtocol; +import org.traccar.CharacterDelimiterFrameDecoder; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; + +public class NetProtocol extends BaseProtocol { + + public NetProtocol() { + addServer(new TrackerServer(false, getName()) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline) { + pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '!')); + pipeline.addLast(new StringEncoder()); + pipeline.addLast(new StringDecoder()); + pipeline.addLast(new NetProtocolDecoder(NetProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/NetProtocolDecoder.java b/src/main/java/org/traccar/protocol/NetProtocolDecoder.java new file mode 100644 index 000000000..61cb29b6b --- /dev/null +++ b/src/main/java/org/traccar/protocol/NetProtocolDecoder.java @@ -0,0 +1,92 @@ +/* + * Copyright 2020 Anton Tananaev (anton@traccar.org) + * + * 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 io.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.DeviceSession; +import org.traccar.Protocol; +import org.traccar.helper.BitUtil; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; +import org.traccar.model.Position; + +import java.net.SocketAddress; +import java.util.regex.Pattern; + +public class NetProtocolDecoder extends BaseProtocolDecoder { + + public NetProtocolDecoder(Protocol protocol) { + super(protocol); + } + + private static final Pattern PATTERN = new PatternBuilder() + .text("@L") + .number("ddd") + .number("(d{15})") // imei + .number("xx") + .number("(dd)(dd)(dd)") // date (ddmmyy) + .number("(dd)(dd)(dd)") // time (hhmmss) + .number("(x)") // flags + .number("(dd)(dd)(dddd)") // latitude + .number("(ddd)(dd)(dddd)") // longitude + .number("(x{8})") // status + .number("(x{4})") // speed + .number("(x{6})") // odometer + .number("(xxx)") // course + .number("(xxx)") // alarm + .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; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS)); + + int flags = parser.nextHexInt(); + + position.setValid(BitUtil.check(flags, 3)); + int hemisphereLatitude = BitUtil.check(flags, 1) ? -1 : 1; + int hemisphereLongitude = BitUtil.check(flags, 0) ? -1 : 1; + + position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_MIN_MIN) * hemisphereLatitude); + position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_MIN_MIN) * hemisphereLongitude); + + position.set(Position.KEY_STATUS, parser.nextHexLong()); + position.setSpeed(parser.nextHexInt() * 0.01); + position.set(Position.KEY_ODOMETER, parser.nextHexInt()); + position.setCourse(parser.nextHexInt()); + + parser.nextHexInt(); // alarm + + return position; + } + +} diff --git a/src/main/java/org/traccar/protocol/NiotProtocolDecoder.java b/src/main/java/org/traccar/protocol/NiotProtocolDecoder.java index 58de0d38f..47c6e2ffd 100644 --- a/src/main/java/org/traccar/protocol/NiotProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/NiotProtocolDecoder.java @@ -31,6 +31,7 @@ import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; public class NiotProtocolDecoder extends BaseProtocolDecoder { @@ -95,12 +96,65 @@ public class NiotProtocolDecoder extends BaseProtocolDecoder { .setSecond(BcdUtil.readInteger(buf, 2)); position.setTime(dateBuilder.getDate()); - position.setValid(true); position.setLatitude(readCoordinate(buf)); position.setLongitude(readCoordinate(buf)); - position.setSpeed(UnitsConverter.knotsFromKph(BcdUtil.readInteger(buf, 4))); + BcdUtil.readInteger(buf, 4); // reserved position.setCourse(BcdUtil.readInteger(buf, 4)); + int statusX = buf.readUnsignedByte(); + position.setValid(BitUtil.check(statusX, 7)); + switch (BitUtil.between(statusX, 3, 5)) { + case 0b10: + position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT); + break; + case 0b01: + position.set(Position.KEY_ALARM, Position.ALARM_LOW_POWER); + break; + default: + break; + } + + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); + + int statusA = buf.readUnsignedByte(); + position.set(Position.KEY_IGNITION, !BitUtil.check(statusA, 7)); + if (!BitUtil.check(statusA, 6)) { + position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED); + } + + buf.readUnsignedByte(); // statusB + buf.readUnsignedByte(); // statusC + position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); + position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 0.1); + position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.1); + buf.readUnsignedByte(); // speed limit + position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte())); + buf.readUnsignedByte(); // sensor speed + buf.readUnsignedByte(); // reserved + buf.readUnsignedByte(); // reserved + + while (buf.readableBytes() > 4) { + int extendedLength = buf.readUnsignedShort(); + int extendedType = buf.readUnsignedShort(); + switch (extendedType) { + case 0x0001: + position.set(Position.KEY_ICCID, + buf.readCharSequence(20, StandardCharsets.US_ASCII).toString()); + break; + case 0x0002: + int statusD = buf.readUnsignedByte(); + position.set(Position.KEY_ALARM, BitUtil.check(statusD, 5) ? Position.ALARM_REMOVING : null); + position.set(Position.KEY_ALARM, BitUtil.check(statusD, 4) ? Position.ALARM_TAMPERING : null); + buf.readUnsignedByte(); // run mode + buf.readUnsignedByte(); // reserved + break; + default: + buf.skipBytes(extendedLength - 2); + break; + } + + } + return position; } diff --git a/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java b/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java index 323d97fa3..509d14ae4 100644 --- a/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java @@ -110,7 +110,7 @@ public class OwnTracksProtocolDecoder extends BaseHttpProtocolDecoder { if (root.containsKey("t")) { String trigger = root.getString("t"); position.set("t", trigger); - Integer reportType = -1; + int reportType = -1; if (root.containsKey("rty")) { reportType = root.getInt("rty"); } @@ -148,8 +148,8 @@ public class OwnTracksProtocolDecoder extends BaseHttpProtocolDecoder { } if (root.containsKey("anum")) { - Integer numberOfAnalogueInputs = root.getInt("anum"); - for (Integer i = 0; i < numberOfAnalogueInputs; i++) { + int numberOfAnalogueInputs = root.getInt("anum"); + for (int i = 0; i < numberOfAnalogueInputs; i++) { String indexString = String.format("%02d", i); if (root.containsKey("adda-" + indexString)) { position.set(Position.PREFIX_ADC + (i + 1), root.getString("adda-" + indexString)); diff --git a/src/main/java/org/traccar/protocol/PolteProtocol.java b/src/main/java/org/traccar/protocol/PolteProtocol.java new file mode 100644 index 000000000..a3e548716 --- /dev/null +++ b/src/main/java/org/traccar/protocol/PolteProtocol.java @@ -0,0 +1,39 @@ +/* + * Copyright 2020 Anton Tananaev (anton@traccar.org) + * + * 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 io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpRequestDecoder; +import io.netty.handler.codec.http.HttpResponseEncoder; +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; + +public class PolteProtocol extends BaseProtocol { + + public PolteProtocol() { + addServer(new TrackerServer(false, getName()) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline) { + pipeline.addLast(new HttpResponseEncoder()); + pipeline.addLast(new HttpRequestDecoder()); + pipeline.addLast(new HttpObjectAggregator(65535)); + pipeline.addLast(new PolteProtocolDecoder(PolteProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/PolteProtocolDecoder.java b/src/main/java/org/traccar/protocol/PolteProtocolDecoder.java new file mode 100644 index 000000000..ce45abef6 --- /dev/null +++ b/src/main/java/org/traccar/protocol/PolteProtocolDecoder.java @@ -0,0 +1,84 @@ +/* + * Copyright 2020 Anton Tananaev (anton@traccar.org) + * + * 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 io.netty.channel.Channel; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpResponseStatus; +import org.traccar.BaseHttpProtocolDecoder; +import org.traccar.DeviceSession; +import org.traccar.Protocol; +import org.traccar.model.Position; + +import javax.json.Json; +import javax.json.JsonObject; +import java.io.StringReader; +import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.Date; + +public class PolteProtocolDecoder extends BaseHttpProtocolDecoder { + + public PolteProtocolDecoder(Protocol protocol) { + super(protocol); + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + FullHttpRequest request = (FullHttpRequest) msg; + String content = request.content().toString(StandardCharsets.UTF_8); + JsonObject json = Json.createReader(new StringReader(content)).readObject(); + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, json.getString("ueToken")); + if (deviceSession == null) { + sendResponse(channel, HttpResponseStatus.BAD_REQUEST); + return null; + } + + if (json.containsKey("location")) { + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + JsonObject location = json.getJsonObject("location"); + + position.setValid(true); + position.setTime(new Date(location.getInt("detected_at") * 1000L)); + position.setLatitude(location.getJsonNumber("latitude").doubleValue()); + position.setLongitude(location.getJsonNumber("longitude").doubleValue()); + position.setAltitude(location.getJsonNumber("altitude").doubleValue()); + + if (json.containsKey("report")) { + JsonObject report = json.getJsonObject("report"); + position.set(Position.KEY_EVENT, report.getInt("event")); + if (report.containsKey("battery")) { + JsonObject battery = report.getJsonObject("battery"); + position.set(Position.KEY_BATTERY_LEVEL, battery.getInt("level")); + position.set(Position.KEY_BATTERY, battery.getJsonNumber("voltage").doubleValue()); + } + } + + return position; + + } + + sendResponse(channel, HttpResponseStatus.OK); + return null; + } + +} diff --git a/src/main/java/org/traccar/protocol/PstFrameEncoder.java b/src/main/java/org/traccar/protocol/PstFrameEncoder.java new file mode 100644 index 000000000..5a9ede3e6 --- /dev/null +++ b/src/main/java/org/traccar/protocol/PstFrameEncoder.java @@ -0,0 +1,39 @@ +/* + * Copyright 2020 Anton Tananaev (anton@traccar.org) + * + * 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 io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; + +public class PstFrameEncoder extends MessageToByteEncoder<ByteBuf> { + + @Override + protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) { + + out.writeByte('('); + while (msg.isReadable()) { + int b = msg.readUnsignedByte(); + if (b == 0x27 || b == 0x28 || b == 0x29) { + out.writeByte(0x27); + out.writeByte(b ^ 0x40); + } else { + out.writeByte(b); + } + } + out.writeByte(')'); + } +} diff --git a/src/main/java/org/traccar/protocol/PstProtocol.java b/src/main/java/org/traccar/protocol/PstProtocol.java index 0ed9affd8..d8c7008cb 100644 --- a/src/main/java/org/traccar/protocol/PstProtocol.java +++ b/src/main/java/org/traccar/protocol/PstProtocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,20 +18,27 @@ package org.traccar.protocol; import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; +import org.traccar.model.Command; public class PstProtocol extends BaseProtocol { public PstProtocol() { + setSupportedDataCommands( + Command.TYPE_ENGINE_STOP, + Command.TYPE_ENGINE_RESUME); addServer(new TrackerServer(true, getName()) { @Override protected void addProtocolHandlers(PipelineBuilder pipeline) { + pipeline.addLast(new PstProtocolEncoder(PstProtocol.this)); pipeline.addLast(new PstProtocolDecoder(PstProtocol.this)); } }); addServer(new TrackerServer(false, getName()) { @Override protected void addProtocolHandlers(PipelineBuilder pipeline) { + pipeline.addLast(new PstFrameEncoder()); pipeline.addLast(new PstFrameDecoder()); + pipeline.addLast(new PstProtocolEncoder(PstProtocol.this)); pipeline.addLast(new PstProtocolDecoder(PstProtocol.this)); } }); diff --git a/src/main/java/org/traccar/protocol/PstProtocolDecoder.java b/src/main/java/org/traccar/protocol/PstProtocolDecoder.java index 62cc203d2..e3fe1af62 100644 --- a/src/main/java/org/traccar/protocol/PstProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/PstProtocolDecoder.java @@ -38,6 +38,7 @@ public class PstProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_ACK = 0x00; public static final int MSG_STATUS = 0x05; + public static final int MSG_COMMAND = 0x06; private Date readDate(ByteBuf buf) { long value = buf.readUnsignedInt(); @@ -61,21 +62,13 @@ public class PstProtocolDecoder extends BaseProtocolDecoder { Channel channel, SocketAddress remoteAddress, long id, int version, long index, int type) { if (channel != null) { - ByteBuf content = Unpooled.buffer(); - content.writeInt((int) id); - content.writeByte(version); - content.writeInt((int) index); - content.writeByte(MSG_ACK); - content.writeByte(type); - - int checksum = Checksum.crc16(Checksum.CRC16_XMODEM, content.nioBuffer()); - ByteBuf response = Unpooled.buffer(); - response.writeByte('('); - response.writeBytes(content); - content.release(); - response.writeShort(checksum); - response.writeByte(')'); + response.writeInt((int) id); + response.writeByte(version); + response.writeInt((int) index); + response.writeByte(MSG_ACK); + response.writeByte(type); + response.writeShort(Checksum.crc16(Checksum.CRC16_XMODEM, response.nioBuffer())); channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); diff --git a/src/main/java/org/traccar/protocol/PstProtocolEncoder.java b/src/main/java/org/traccar/protocol/PstProtocolEncoder.java new file mode 100644 index 000000000..f3d193324 --- /dev/null +++ b/src/main/java/org/traccar/protocol/PstProtocolEncoder.java @@ -0,0 +1,62 @@ +/* + * Copyright 2020 Anton Tananaev (anton@traccar.org) + * + * 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 io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import org.traccar.BaseProtocolEncoder; +import org.traccar.Protocol; +import org.traccar.helper.Checksum; +import org.traccar.model.Command; + +public class PstProtocolEncoder extends BaseProtocolEncoder { + + public PstProtocolEncoder(Protocol protocol) { + super(protocol); + } + + private ByteBuf encodeContent(long deviceId, int type, int data1, int data2) { + + ByteBuf buf = Unpooled.buffer(); + + buf.writeInt((int) Long.parseLong(getUniqueId(deviceId))); + buf.writeByte(0x06); // version + + buf.writeInt(1); // index + buf.writeByte(PstProtocolDecoder.MSG_COMMAND); + buf.writeShort(type); + buf.writeShort(data1); + buf.writeShort(data2); + + buf.writeShort(Checksum.crc16(Checksum.CRC16_XMODEM, buf.nioBuffer())); + + return buf; + } + + @Override + protected Object encodeCommand(Command command) { + + switch (command.getType()) { + case Command.TYPE_ENGINE_STOP: + return encodeContent(command.getDeviceId(), 0x0002, 0xffff, 0xffff); + case Command.TYPE_ENGINE_RESUME: + return encodeContent(command.getDeviceId(), 0x0001, 0xffff, 0xffff); + default: + return null; + } + } + +} diff --git a/src/main/java/org/traccar/protocol/Pt502ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Pt502ProtocolDecoder.java index 0afec67ad..ff92b51f1 100644 --- a/src/main/java/org/traccar/protocol/Pt502ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Pt502ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org) * Copyright 2012 Luis Parada (luis.parada@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -55,8 +55,8 @@ public class Pt502ProtocolDecoder extends BaseProtocolDecoder { .expression("([EW]),") .number("(d+.d+)?,") // speed .number("(d+.d+)?,") // course - .number("(dd)(dd)(dd),,,") // date (ddmmyy) - .expression("./") + .number("(dd)(dd)(dd),,,?") // date (ddmmyy) + .expression(".?/") .expression("([01])+,") // input .expression("([01])+/") // output .expression("([^/]+)?/") // adc @@ -108,7 +108,7 @@ public class Pt502ProtocolDecoder extends BaseProtocolDecoder { position.setDeviceId(deviceSession.getDeviceId()); DateBuilder dateBuilder = new DateBuilder() - .setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0), parser.nextInt(0)); + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextInt()); position.setValid(parser.next().equals("A")); position.setLatitude(parser.nextCoordinate()); @@ -116,7 +116,7 @@ public class Pt502ProtocolDecoder extends BaseProtocolDecoder { position.setSpeed(parser.nextDouble(0)); position.setCourse(parser.nextDouble(0)); - dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0)); + dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt()); position.setTime(dateBuilder.getDate()); position.set(Position.KEY_INPUT, parser.next()); diff --git a/src/main/java/org/traccar/protocol/RadarProtocolDecoder.java b/src/main/java/org/traccar/protocol/RadarProtocolDecoder.java index b56a081c7..d87f77b84 100644 --- a/src/main/java/org/traccar/protocol/RadarProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/RadarProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -70,6 +70,7 @@ public class RadarProtocolDecoder extends BaseProtocolDecoder { for (int index = 0; index < count; index++) { Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); position.set(Position.KEY_EVENT, buf.readUnsignedShort()); diff --git a/src/main/java/org/traccar/protocol/SpotProtocolDecoder.java b/src/main/java/org/traccar/protocol/SpotProtocolDecoder.java index da36c2048..34417d95f 100644 --- a/src/main/java/org/traccar/protocol/SpotProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/SpotProtocolDecoder.java @@ -42,9 +42,9 @@ import java.util.List; public class SpotProtocolDecoder extends BaseHttpProtocolDecoder { - private DocumentBuilder documentBuilder; - private XPath xPath; - private XPathExpression messageExpression; + private final DocumentBuilder documentBuilder; + private final XPath xPath; + private final XPathExpression messageExpression; public SpotProtocolDecoder(Protocol protocol) { super(protocol); diff --git a/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java b/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java index 2d1613e03..7ba41ad56 100644 --- a/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java @@ -20,11 +20,13 @@ import org.traccar.BaseProtocolDecoder; import org.traccar.Context; import org.traccar.DeviceSession; import org.traccar.Protocol; +import org.traccar.helper.DataConverter; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; import org.traccar.model.CellTower; import org.traccar.model.Network; import org.traccar.model.Position; +import org.traccar.protobuf.StarLinkMessage; import java.net.SocketAddress; import java.text.DateFormat; @@ -47,8 +49,8 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder { .number("xx") // checksum .compile(); - private String[] dataTags; - private DateFormat dateFormat; + private String format; + private String dateFormat; public StarLinkProtocolDecoder(Protocol protocol) { super(protocol); @@ -60,13 +62,24 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder { setDateFormat(Context.getConfig().getString(getProtocolName() + ".dateFormat", "yyMMddHHmmss")); } + public String[] getFormat(long deviceId) { + return Context.getIdentityManager().lookupAttributeString( + deviceId, getProtocolName() + ".format", format, false, false).split(","); + } + public void setFormat(String format) { - dataTags = format.split(","); + this.format = format; + } + + public DateFormat getDateFormat(long deviceId) { + DateFormat dateFormat = new SimpleDateFormat(Context.getIdentityManager().lookupAttributeString( + deviceId, getProtocolName() + ".dateFormat", this.dateFormat, false, false)); + dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + return dateFormat; } public void setDateFormat(String dateFormat) { - this.dateFormat = new SimpleDateFormat(dateFormat); - this.dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + this.dateFormat = dateFormat; } private double parseCoordinate(String value) { @@ -128,6 +141,9 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder { Integer lac = null, cid = null; int event = 0; + String[] dataTags = getFormat(deviceSession.getDeviceId()); + DateFormat dateFormat = getDateFormat(deviceSession.getDeviceId()); + for (int i = 0; i < Math.min(data.length, dataTags.length); i++) { if (data[i].isEmpty()) { continue; @@ -186,6 +202,9 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder { cid = Integer.parseInt(data[i]); } break; + case "#CSS#": + position.set(Position.KEY_RSSI, Integer.parseInt(data[i])); + break; case "#VIN#": position.set(Position.KEY_POWER, Double.parseDouble(data[i])); break; @@ -201,6 +220,32 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder { case "#ENG#": position.set("engine", data[i].equals("1")); break; + case "#SATU#": + position.set(Position.KEY_SATELLITES, Integer.parseInt(data[i])); + break; + case "#TS1#": + position.set("sensor1State", Integer.parseInt(data[i])); + break; + case "#TS2#": + position.set("sensor2State", Integer.parseInt(data[i])); + break; + case "#TD1#": + case "#TD2#": + StarLinkMessage.mEventReport_TDx message = + StarLinkMessage.mEventReport_TDx.parseFrom(DataConverter.parseBase64(data[i])); + position.set( + "sensor" + message.getSensorNumber() + "Id", + message.getSensorID()); + position.set( + "sensor" + message.getSensorNumber() + "Temp", + message.getTemperature() * 0.1); + position.set( + "sensor" + message.getSensorNumber() + "Humidity", + message.getTemperature() * 0.1); + position.set( + "sensor" + message.getSensorNumber() + "Voltage", + message.getVoltage() * 0.001); + break; default: break; } diff --git a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java index 451b9ba32..76e3e6ecc 100644 --- a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java @@ -479,20 +479,21 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { position.setTime(dateFormat.parse(values[index++] + values[index++])); } + CellTower cellTower = new CellTower(); if (BitUtil.check(mask, 6)) { - index += 1; // cell + cellTower.setCellId(Long.parseLong(values[index++], 16)); } - if (BitUtil.check(mask, 7)) { - index += 1; // mcc + cellTower.setMobileCountryCode(Integer.parseInt(values[index++])); } - if (BitUtil.check(mask, 8)) { - index += 1; // mnc + cellTower.setMobileNetworkCode(Integer.parseInt(values[index++])); } - if (BitUtil.check(mask, 9)) { - index += 1; // lac + cellTower.setLocationAreaCode(Integer.parseInt(values[index++], 16)); + } + if (cellTower.getCellId() != null) { + position.setNetwork(new Network(cellTower)); } if (BitUtil.check(mask, 10)) { @@ -531,16 +532,26 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_OUTPUT, Integer.parseInt(values[index++])); } - if (BitUtil.check(mask, 19)) { - position.set("alertId", values[index++]); - } - - if (BitUtil.check(mask, 20)) { - position.set("alertModifier", values[index++]); - } - - if (BitUtil.check(mask, 21)) { - position.set("alertData", values[index++]); + if (type.equals("ALT")) { + if (BitUtil.check(mask, 19)) { + position.set("alertId", values[index++]); + } + if (BitUtil.check(mask, 20)) { + position.set("alertModifier", values[index++]); + } + if (BitUtil.check(mask, 21)) { + position.set("alertData", values[index++]); + } + } else { + if (BitUtil.check(mask, 19)) { + position.set("mode", Integer.parseInt(values[index++])); + } + if (BitUtil.check(mask, 20)) { + position.set("reason", Integer.parseInt(values[index++])); + } + if (BitUtil.check(mask, 21)) { + position.set(Position.KEY_INDEX, Integer.parseInt(values[index++])); + } } if (BitUtil.check(mask, 22)) { @@ -676,6 +687,24 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { return position; } + private Position decodeTravelReport(Channel channel, SocketAddress remoteAddress, String[] values) { + int index = 1; + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + getLastLocation(position, null); + + position.set(Position.KEY_DRIVER_UNIQUE_ID, values[values.length - 1]); + + return position; + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { @@ -693,6 +722,8 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { if (prefix.length() < 5) { return decodeUniversal(channel, remoteAddress, values); + } else if (prefix.endsWith("HTE")) { + return decodeTravelReport(channel, remoteAddress, values); } else if (prefix.startsWith("ST9")) { return decode9(channel, remoteAddress, values); } else if (prefix.startsWith("ST4")) { diff --git a/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java b/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java index b75addfae..230d29216 100644 --- a/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ import org.traccar.Protocol; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; +import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; import java.net.SocketAddress; @@ -109,6 +110,20 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { .any() .compile(); + private static final Pattern PATTERN_QZE = new PatternBuilder() + .text("QZE,") + .number("(d{15}),") // imei + .number("(d+),") // event + .number("(dd)(dd)(dddd),") // date (mmddyyyy) + .number("(dd)(dd)(dd),") // time (hhmmss) + .number("(-?d+.d+),") // latitude + .number("(-?d+.d+),") // longitude + .number("(d+),") // speed + .number("(d+),") // course + .expression("([AV]),") // validity + .expression("([01])") // ignition + .compile(); + private Position position = null; private Position decodeGprmc( @@ -256,6 +271,35 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { return position; } + private Position decodeQze(Channel channel, SocketAddress remoteAddress, String sentence) { + + Parser parser = new Parser(PATTERN_QZE, sentence); + if (!parser.matches()) { + return null; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.set(Position.KEY_EVENT, parser.nextInt()); + + position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS)); + position.setLatitude(parser.nextDouble()); + position.setLongitude(parser.nextDouble()); + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt())); + position.setCourse(parser.nextInt()); + position.setValid(parser.next().equals("A")); + + position.set(Position.KEY_IGNITION, parser.nextInt() > 0); + + return position; + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { @@ -308,6 +352,8 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { return decodeTrccr(deviceSession, sentence); } else if (sentence.startsWith("$GPIOP")) { return decodeGpiop(deviceSession, sentence); + } else if (sentence.startsWith("QZE")) { + return decodeQze(channel, remoteAddress, sentence); } return null; diff --git a/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java b/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java index 3331ebb71..5822d6b8f 100644 --- a/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java @@ -230,10 +230,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); int battery = BcdUtil.readInteger(buf, 2); - if (battery == 0) { - battery = 100; - } - position.set(Position.KEY_BATTERY, battery); + position.set(Position.KEY_BATTERY_LEVEL, battery > 0 ? battery : 100); } @@ -284,7 +281,8 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder { } position.set(Position.KEY_G_SENSOR, "[" + accelerationX + "," + accelerationY + "," + accelerationZ + "]"); - position.set(Position.KEY_BATTERY_LEVEL, BcdUtil.readInteger(buf, 2)); + int battery = BcdUtil.readInteger(buf, 2); + position.set(Position.KEY_BATTERY_LEVEL, battery > 0 ? battery : 100); position.set(Position.KEY_DEVICE_TEMP, (int) buf.readByte()); position.set("lightSensor", BcdUtil.readInteger(buf, 2) * 0.1); position.set(Position.KEY_BATTERY, BcdUtil.readInteger(buf, 2) * 0.1); diff --git a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java index 40b769869..0ea02b157 100644 --- a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java @@ -43,9 +43,9 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { private static final int IMAGE_PACKET_MAX = 2048; - private boolean connectionless; + private final boolean connectionless; private boolean extended; - private Map<Long, ByteBuf> photos = new HashMap<>(); + private final Map<Long, ByteBuf> photos = new HashMap<>(); public void setExtended(boolean extended) { this.extended = extended; @@ -208,7 +208,6 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { position.set(Position.PREFIX_ADC + 2, readValue(buf, length, false)); break; case 16: - case 87: position.set(Position.KEY_ODOMETER, readValue(buf, length, false)); break; case 17: @@ -223,9 +222,6 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { case 21: position.set(Position.KEY_RSSI, readValue(buf, length, false)); break; - case 24: - readValue(buf, length, false); // speed - break; case 25: case 26: case 27: @@ -255,34 +251,9 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { case 80: position.set("workMode", readValue(buf, length, false)); break; - case 81: - position.set(Position.KEY_OBD_SPEED, readValue(buf, length, false)); - break; - case 82: - position.set(Position.KEY_THROTTLE, readValue(buf, length, false)); - break; - case 83: - position.set(Position.KEY_FUEL_USED, readValue(buf, length, false) * 0.1); - break; - case 84: - position.set(Position.KEY_FUEL_LEVEL, readValue(buf, length, false) * 0.1); - break; - case 85: - position.set(Position.KEY_RPM, readValue(buf, length, false)); - break; case 90: position.set(Position.KEY_DOOR, readValue(buf, length, false)); break; - case 110: - position.set(Position.KEY_FUEL_CONSUMPTION, readValue(buf, length, true) * 0.1); - break; - case 113: - if (length == 1) { - position.set(Position.KEY_BATTERY_LEVEL, readValue(buf, length, true)); - } else { - position.set(Position.PREFIX_IO + id, readValue(buf, length, false)); - } - break; case 115: position.set(Position.KEY_COOLANT_TEMP, readValue(buf, length, true) * 0.1); break; @@ -311,9 +282,6 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { case 199: position.set(Position.KEY_ODOMETER_TRIP, readValue(buf, length, false)); break; - case 235: - position.set("oilLevel", readValue(buf, length, false)); - break; case 236: if (readValue(buf, length, false) == 1) { position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED); @@ -569,6 +537,29 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { int length = buf.readUnsignedShort(); if (id == 256) { position.set(Position.KEY_VIN, buf.readSlice(length).toString(StandardCharsets.US_ASCII)); + } else if (id == 385) { + ByteBuf data = buf.readSlice(length); + data.readUnsignedByte(); // data part + int index = 1; + while (data.isReadable()) { + int flags = data.readUnsignedByte(); + if (BitUtil.from(flags, 4) > 0) { + position.set("beacon" + index + "Uuid", ByteBufUtil.hexDump(data.readSlice(16))); + position.set("beacon" + index + "Major", data.readUnsignedShort()); + position.set("beacon" + index + "Minor", data.readUnsignedShort()); + } else { + position.set("beacon" + index + "Namespace", ByteBufUtil.hexDump(data.readSlice(10))); + position.set("beacon" + index + "Instance", ByteBufUtil.hexDump(data.readSlice(6))); + } + position.set("beacon" + index + "Rssi", (int) data.readByte()); + if (BitUtil.check(flags, 1)) { + position.set("beacon" + index + "Battery", data.readUnsignedShort() * 0.01); + } + if (BitUtil.check(flags, 2)) { + position.set("beacon" + index + "Temp", data.readUnsignedShort()); + } + index += 1; + } } else { position.set(Position.PREFIX_IO + id, ByteBufUtil.hexDump(buf.readSlice(length))); } diff --git a/src/main/java/org/traccar/protocol/V680ProtocolDecoder.java b/src/main/java/org/traccar/protocol/V680ProtocolDecoder.java index 0342404a6..40267022b 100644 --- a/src/main/java/org/traccar/protocol/V680ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/V680ProtocolDecoder.java @@ -62,7 +62,7 @@ public class V680ProtocolDecoder extends BaseProtocolDecoder { if (sentence.length() == 16) { - getDeviceSession(channel, remoteAddress, sentence.substring(1, sentence.length())); + getDeviceSession(channel, remoteAddress, sentence.substring(1)); } else { diff --git a/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java index c132f194b..31a1bbb11 100644 --- a/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java @@ -91,7 +91,7 @@ public class Xt2400ProtocolDecoder extends BaseProtocolDecoder { return length; } - private Map<Short, byte[]> formats = new HashMap<>(); + private final Map<Short, byte[]> formats = new HashMap<>(); public void setConfig(String configString) { Pattern pattern = Pattern.compile(":wycfg pcr\\[\\d+] ([0-9a-fA-F]{2})[0-9a-fA-F]{2}([0-9a-fA-F]+)"); diff --git a/src/main/java/org/traccar/reports/ReportUtils.java b/src/main/java/org/traccar/reports/ReportUtils.java index 3a631e0d9..870264fad 100644 --- a/src/main/java/org/traccar/reports/ReportUtils.java +++ b/src/main/java/org/traccar/reports/ReportUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org) * Copyright 2016 - 2017 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -81,8 +81,7 @@ public final class ReportUtils { } public static Collection<Long> getDeviceList(Collection<Long> deviceIds, Collection<Long> groupIds) { - Collection<Long> result = new ArrayList<>(); - result.addAll(deviceIds); + Collection<Long> result = new ArrayList<>(deviceIds); for (long groupId : groupIds) { result.addAll(Context.getPermissionsManager().getGroupDevices(groupId)); } @@ -98,7 +97,7 @@ public final class ReportUtils { double firstOdometer = firstPosition.getDouble(Position.KEY_ODOMETER); double lastOdometer = lastPosition.getDouble(Position.KEY_ODOMETER); - if (useOdometer && (firstOdometer != 0.0 || lastOdometer != 0.0)) { + if (useOdometer && firstOdometer != 0.0 && lastOdometer != 0.0) { distance = lastOdometer - firstOdometer; } else if (firstPosition.getAttributes().containsKey(Position.KEY_TOTAL_DISTANCE) && lastPosition.getAttributes().containsKey(Position.KEY_TOTAL_DISTANCE)) { @@ -113,7 +112,7 @@ public final class ReportUtils { if (firstPosition.getAttributes().get(Position.KEY_FUEL_LEVEL) != null && lastPosition.getAttributes().get(Position.KEY_FUEL_LEVEL) != null) { - BigDecimal value = new BigDecimal(firstPosition.getDouble(Position.KEY_FUEL_LEVEL) + BigDecimal value = BigDecimal.valueOf(firstPosition.getDouble(Position.KEY_FUEL_LEVEL) - lastPosition.getDouble(Position.KEY_FUEL_LEVEL)); return value.setScale(1, RoundingMode.HALF_EVEN).doubleValue(); } @@ -264,21 +263,10 @@ public final class ReportUtils { stop.setDuration(stopDuration); stop.setSpentFuel(calculateFuel(startStop, endStop)); - long engineHours = 0; if (startStop.getAttributes().containsKey(Position.KEY_HOURS) && endStop.getAttributes().containsKey(Position.KEY_HOURS)) { - engineHours = endStop.getLong(Position.KEY_HOURS) - startStop.getLong(Position.KEY_HOURS); - } else if (Context.getConfig().getBoolean("processing.engineHours.enable")) { - // Temporary fallback for old data, to be removed in May 2019 - for (int i = startIndex + 1; i <= endIndex; i++) { - if (positions.get(i).getBoolean(Position.KEY_IGNITION) - && positions.get(i - 1).getBoolean(Position.KEY_IGNITION)) { - engineHours += positions.get(i).getFixTime().getTime() - - positions.get(i - 1).getFixTime().getTime(); - } - } + stop.setEngineHours(endStop.getLong(Position.KEY_HOURS) - startStop.getLong(Position.KEY_HOURS)); } - stop.setEngineHours(engineHours); if (!ignoreOdometer && startStop.getDouble(Position.KEY_ODOMETER) != 0 diff --git a/src/main/java/org/traccar/reports/Summary.java b/src/main/java/org/traccar/reports/Summary.java index 6d179a873..612761844 100644 --- a/src/main/java/org/traccar/reports/Summary.java +++ b/src/main/java/org/traccar/reports/Summary.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org) * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,6 +22,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Calendar; import java.util.Collection; import java.util.Date; @@ -35,27 +36,18 @@ public final class Summary { private Summary() { } - private static SummaryReport calculateSummaryResult(long deviceId, Date from, Date to) throws SQLException { + private static SummaryReport calculateSummaryResult(long deviceId, Collection<Position> positions) { SummaryReport result = new SummaryReport(); result.setDeviceId(deviceId); result.setDeviceName(Context.getIdentityManager().getById(deviceId).getName()); - Collection<Position> positions = Context.getDataManager().getPositions(deviceId, from, to); if (positions != null && !positions.isEmpty()) { Position firstPosition = null; Position previousPosition = null; double speedSum = 0; - boolean engineHoursEnabled = Context.getConfig().getBoolean("processing.engineHours.enable"); for (Position position : positions) { if (firstPosition == null) { firstPosition = position; } - if (engineHoursEnabled && previousPosition != null - && position.getBoolean(Position.KEY_IGNITION) - && previousPosition.getBoolean(Position.KEY_IGNITION)) { - // Temporary fallback for old data, to be removed in May 2019 - result.addEngineHours(position.getFixTime().getTime() - - previousPosition.getFixTime().getTime()); - } previousPosition = position; speedSum += position.getSpeed(); result.setMaxSpeed(position.getSpeed()); @@ -66,8 +58,7 @@ public final class Summary { result.setAverageSpeed(speedSum / positions.size()); result.setSpentFuel(ReportUtils.calculateFuel(firstPosition, previousPosition)); - if (engineHoursEnabled - && firstPosition.getAttributes().containsKey(Position.KEY_HOURS) + if (firstPosition.getAttributes().containsKey(Position.KEY_HOURS) && previousPosition.getAttributes().containsKey(Position.KEY_HOURS)) { result.setEngineHours( previousPosition.getLong(Position.KEY_HOURS) - firstPosition.getLong(Position.KEY_HOURS)); @@ -83,26 +74,59 @@ public final class Summary { result.setEndOdometer(previousPosition.getDouble(Position.KEY_TOTAL_DISTANCE)); } + result.setStartTime(firstPosition.getFixTime()); + result.setEndTime(previousPosition.getServerTime()); } return result; } + private static int getDay(long userId, Date date) { + Calendar calendar = Calendar.getInstance(ReportUtils.getTimezone(userId)); + calendar.setTime(date); + return calendar.get(Calendar.DAY_OF_MONTH); + } + + private static Collection<SummaryReport> calculateSummaryResults( + long userId, long deviceId, Date from, Date to, boolean daily) throws SQLException { + + ArrayList<Position> positions = new ArrayList<>(Context.getDataManager().getPositions(deviceId, from, to)); + + ArrayList<SummaryReport> results = new ArrayList<>(); + if (daily && !positions.isEmpty()) { + int startIndex = 0; + int startDay = getDay(userId, positions.iterator().next().getFixTime()); + for (int i = 0; i < positions.size(); i++) { + int currentDay = getDay(userId, positions.get(i).getFixTime()); + if (currentDay != startDay) { + results.add(calculateSummaryResult(deviceId, positions.subList(startIndex, i))); + startIndex = i; + startDay = currentDay; + } + } + results.add(calculateSummaryResult(deviceId, positions.subList(startIndex, positions.size()))); + } else { + results.add(calculateSummaryResult(deviceId, positions)); + } + + return results; + } + public static Collection<SummaryReport> getObjects(long userId, Collection<Long> deviceIds, - Collection<Long> groupIds, Date from, Date to) throws SQLException { + Collection<Long> groupIds, Date from, Date to, boolean daily) throws SQLException { ReportUtils.checkPeriodLimit(from, to); ArrayList<SummaryReport> result = new ArrayList<>(); for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) { Context.getPermissionsManager().checkDevice(userId, deviceId); - result.add(calculateSummaryResult(deviceId, from, to)); + result.addAll(calculateSummaryResults(userId, deviceId, from, to, daily)); } return result; } public static void getExcel(OutputStream outputStream, long userId, Collection<Long> deviceIds, Collection<Long> groupIds, - Date from, Date to) throws SQLException, IOException { + Date from, Date to, boolean daily) throws SQLException, IOException { ReportUtils.checkPeriodLimit(from, to); - Collection<SummaryReport> summaries = getObjects(userId, deviceIds, groupIds, from, to); + Collection<SummaryReport> summaries = getObjects(userId, deviceIds, groupIds, from, to, daily); String templatePath = Context.getConfig().getString("report.templatesPath", "templates/export/"); try (InputStream inputStream = new FileInputStream(templatePath + "/summary.xlsx")) { diff --git a/src/main/java/org/traccar/reports/model/BaseReport.java b/src/main/java/org/traccar/reports/model/BaseReport.java index 9f2d1188c..deb42bb8a 100644 --- a/src/main/java/org/traccar/reports/model/BaseReport.java +++ b/src/main/java/org/traccar/reports/model/BaseReport.java @@ -16,6 +16,8 @@ */ package org.traccar.reports.model; +import java.util.Date; + public class BaseReport { private long deviceId; @@ -103,4 +105,24 @@ public class BaseReport { this.endOdometer = endOdometer; } + private Date startTime; + + public Date getStartTime() { + return startTime; + } + + public void setStartTime(Date startTime) { + this.startTime = startTime; + } + + private Date endTime; + + public Date getEndTime() { + return endTime; + } + + public void setEndTime(Date endTime) { + this.endTime = endTime; + } + } diff --git a/src/main/java/org/traccar/reports/model/StopReport.java b/src/main/java/org/traccar/reports/model/StopReport.java index 245292b63..e20f2c503 100644 --- a/src/main/java/org/traccar/reports/model/StopReport.java +++ b/src/main/java/org/traccar/reports/model/StopReport.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org) * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,8 +16,6 @@ */ package org.traccar.reports.model; -import java.util.Date; - public class StopReport extends BaseReport { private long positionId; @@ -50,26 +48,6 @@ public class StopReport extends BaseReport { this.longitude = longitude; } - private Date startTime; - - public Date getStartTime() { - return startTime; - } - - public void setStartTime(Date startTime) { - this.startTime = startTime; - } - - private Date endTime; - - public Date getEndTime() { - return endTime; - } - - public void setEndTime(Date endTime) { - this.endTime = endTime; - } - private String address; public String getAddress() { @@ -99,8 +77,4 @@ public class StopReport extends BaseReport { public void setEngineHours(long engineHours) { this.engineHours = engineHours; } - - public void addEngineHours(long engineHours) { - this.engineHours += engineHours; - } } diff --git a/src/main/java/org/traccar/reports/model/SummaryReport.java b/src/main/java/org/traccar/reports/model/SummaryReport.java index 6f9e9459f..886f8b9e2 100644 --- a/src/main/java/org/traccar/reports/model/SummaryReport.java +++ b/src/main/java/org/traccar/reports/model/SummaryReport.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org) * Copyright 2016 - 2017 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -27,8 +27,4 @@ public class SummaryReport extends BaseReport { public void setEngineHours(long engineHours) { this.engineHours = engineHours; } - - public void addEngineHours(long engineHours) { - this.engineHours += engineHours; - } } diff --git a/src/main/java/org/traccar/reports/model/TripReport.java b/src/main/java/org/traccar/reports/model/TripReport.java index 3140f3019..151c34bd5 100644 --- a/src/main/java/org/traccar/reports/model/TripReport.java +++ b/src/main/java/org/traccar/reports/model/TripReport.java @@ -16,8 +16,6 @@ */ package org.traccar.reports.model; -import java.util.Date; - public class TripReport extends BaseReport { private long startPositionId; @@ -80,16 +78,6 @@ public class TripReport extends BaseReport { this.endLon = endLon; } - private Date startTime; - - public Date getStartTime() { - return startTime; - } - - public void setStartTime(Date startTime) { - this.startTime = startTime; - } - private String startAddress; public String getStartAddress() { @@ -100,16 +88,6 @@ public class TripReport extends BaseReport { this.startAddress = address; } - private Date endTime; - - public Date getEndTime() { - return endTime; - } - - public void setEndTime(Date endTime) { - this.endTime = endTime; - } - private String endAddress; public String getEndAddress() { diff --git a/src/main/java/org/traccar/schedule/ScheduleManager.java b/src/main/java/org/traccar/schedule/ScheduleManager.java new file mode 100644 index 000000000..4def211d0 --- /dev/null +++ b/src/main/java/org/traccar/schedule/ScheduleManager.java @@ -0,0 +1,42 @@ +/* + * Copyright 2020 Anton Tananaev (anton@traccar.org) + * + * 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.schedule; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +public class ScheduleManager { + + private ScheduledExecutorService executor; + + public void start() { + + executor = Executors.newSingleThreadScheduledExecutor(); + + new TaskDeviceInactivityCheck().schedule(executor); + + } + + public void stop() { + + if (executor != null) { + executor.shutdown(); + executor = null; + } + + } + +} diff --git a/src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java b/src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java new file mode 100644 index 000000000..80641d7d4 --- /dev/null +++ b/src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java @@ -0,0 +1,79 @@ +/* + * Copyright 2020 Anton Tananaev (anton@traccar.org) + * + * 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.schedule; + +import org.traccar.Context; +import org.traccar.model.Device; +import org.traccar.model.Event; +import org.traccar.model.Position; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public class TaskDeviceInactivityCheck implements Runnable { + + public static final String ATTRIBUTE_DEVICE_INACTIVITY_START = "deviceInactivityStart"; + public static final String ATTRIBUTE_DEVICE_INACTIVITY_PERIOD = "deviceInactivityPeriod"; + public static final String ATTRIBUTE_LAST_UPDATE = "lastUpdate"; + + private static final long CHECK_PERIOD_MINUTES = 15; + + public void schedule(ScheduledExecutorService executor) { + executor.scheduleAtFixedRate(this, CHECK_PERIOD_MINUTES, CHECK_PERIOD_MINUTES, TimeUnit.MINUTES); + } + + @Override + public void run() { + long currentTime = System.currentTimeMillis(); + long checkPeriod = TimeUnit.MINUTES.toMillis(CHECK_PERIOD_MINUTES); + + Map<Event, Position> events = new HashMap<>(); + for (Device device : Context.getDeviceManager().getAllDevices()) { + if (device.getLastUpdate() != null && checkDevice(device, currentTime, checkPeriod)) { + Event event = new Event(Event.TYPE_DEVICE_INACTIVE, device.getId()); + event.set(ATTRIBUTE_LAST_UPDATE, device.getLastUpdate().getTime()); + events.put(event, null); + } + } + + Context.getNotificationManager().updateEvents(events); + } + + private boolean checkDevice(Device device, long currentTime, long checkPeriod) { + long deviceInactivityStart = device.getLong(ATTRIBUTE_DEVICE_INACTIVITY_START); + if (deviceInactivityStart > 0) { + long timeThreshold = device.getLastUpdate().getTime() + deviceInactivityStart; + if (currentTime >= timeThreshold) { + + if (currentTime - checkPeriod < timeThreshold) { + return true; + } + + long deviceInactivityPeriod = device.getLong(ATTRIBUTE_DEVICE_INACTIVITY_PERIOD); + if (deviceInactivityPeriod > 0) { + long count = (currentTime - timeThreshold - 1) / deviceInactivityPeriod; + timeThreshold += count * deviceInactivityPeriod; + return currentTime - checkPeriod < timeThreshold; + } + + } + } + return false; + } + +} diff --git a/src/main/java/org/traccar/sms/smpp/SmppClient.java b/src/main/java/org/traccar/sms/smpp/SmppClient.java index 874253d36..6eb850b6a 100644 --- a/src/main/java/org/traccar/sms/smpp/SmppClient.java +++ b/src/main/java/org/traccar/sms/smpp/SmppClient.java @@ -20,7 +20,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; @@ -122,25 +121,19 @@ public class SmppClient implements SmsManager { enquireLinkPeriod = Context.getConfig().getInteger("sms.smpp.enquireLinkPeriod", 60000); enquireLinkTimeout = Context.getConfig().getInteger("sms.smpp.enquireLinkTimeout", 10000); - enquireLinkExecutor = Executors.newScheduledThreadPool(1, new ThreadFactory() { - @Override - public Thread newThread(Runnable runnable) { - Thread thread = new Thread(runnable); - String name = sessionConfig.getName(); - thread.setName("EnquireLink-" + name); - return thread; - } + enquireLinkExecutor = Executors.newScheduledThreadPool(1, runnable -> { + Thread thread = new Thread(runnable); + String name = sessionConfig.getName(); + thread.setName("EnquireLink-" + name); + return thread; }); reconnectionDelay = Context.getConfig().getInteger("sms.smpp.reconnectionDelay", 10000); - reconnectionExecutor = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { - @Override - public Thread newThread(Runnable runnable) { - Thread thread = new Thread(runnable); - String name = sessionConfig.getName(); - thread.setName("Reconnection-" + name); - return thread; - } + reconnectionExecutor = Executors.newSingleThreadScheduledExecutor(runnable -> { + Thread thread = new Thread(runnable); + String name = sessionConfig.getName(); + thread.setName("Reconnection-" + name); + return thread; }); scheduleReconnect(); @@ -257,14 +250,11 @@ public class SmppClient implements SmsManager { @Override public void sendMessageAsync(final String destAddress, final String message, final boolean command) { - executorService.execute(new Runnable() { - @Override - public void run() { - try { - sendMessageSync(destAddress, message, command); - } catch (MessageException | InterruptedException | IllegalStateException error) { - LOGGER.warn("SMS sending error", error); - } + executorService.execute(() -> { + try { + sendMessageSync(destAddress, message, command); + } catch (MessageException | InterruptedException | IllegalStateException error) { + LOGGER.warn("SMS sending error", error); } }); } diff --git a/src/main/java/org/traccar/speedlimit/OverpassSpeedLimitProvider.java b/src/main/java/org/traccar/speedlimit/OverpassSpeedLimitProvider.java new file mode 100644 index 000000000..429a47c76 --- /dev/null +++ b/src/main/java/org/traccar/speedlimit/OverpassSpeedLimitProvider.java @@ -0,0 +1,74 @@ +/* + * Copyright 2020 Anton Tananaev (anton@traccar.org) + * + * 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.speedlimit; + +import org.traccar.Context; +import org.traccar.helper.UnitsConverter; + +import javax.json.JsonArray; +import javax.json.JsonObject; +import javax.ws.rs.client.AsyncInvoker; +import javax.ws.rs.client.InvocationCallback; + +public class OverpassSpeedLimitProvider implements SpeedLimitProvider { + + private final String url; + + public OverpassSpeedLimitProvider(String url) { + this.url = url + "?data=[out:json];way[maxspeed](around:100.0,%f,%f);out%%20tags;"; + } + + private Double parseSpeed(String value) { + if (value.endsWith(" mph")) { + return UnitsConverter.knotsFromMph(Double.parseDouble(value.substring(0, value.length() - 4))); + } else if (value.endsWith(" knots")) { + return Double.parseDouble(value.substring(0, value.length() - 6)); + } else if (value.matches("\\d+")) { + return UnitsConverter.knotsFromKph(Double.parseDouble(value)); + } else { + return null; + } + } + + @Override + public void getSpeedLimit(double latitude, double longitude, SpeedLimitProviderCallback callback) { + String formattedUrl = String.format(url, latitude, longitude); + AsyncInvoker invoker = Context.getClient().target(formattedUrl).request().async(); + invoker.get(new InvocationCallback<JsonObject>() { + @Override + public void completed(JsonObject json) { + JsonArray elements = json.getJsonArray("elements"); + if (!elements.isEmpty()) { + Double maxSpeed = parseSpeed( + elements.getJsonObject(0).getJsonObject("tags").getString("maxspeed")); + if (maxSpeed != null) { + callback.onSuccess(maxSpeed); + } else { + callback.onFailure(new SpeedLimitException("Parsing failed")); + } + } else { + callback.onFailure(new SpeedLimitException("Not found")); + } + } + + @Override + public void failed(Throwable throwable) { + callback.onFailure(throwable); + } + }); + } + +} diff --git a/src/main/java/org/traccar/speedlimit/SpeedLimitException.java b/src/main/java/org/traccar/speedlimit/SpeedLimitException.java new file mode 100644 index 000000000..453c952d3 --- /dev/null +++ b/src/main/java/org/traccar/speedlimit/SpeedLimitException.java @@ -0,0 +1,24 @@ +/* + * Copyright 2020 Anton Tananaev (anton@traccar.org) + * + * 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.speedlimit; + +public class SpeedLimitException extends RuntimeException { + + public SpeedLimitException(String message) { + super(message); + } + +} diff --git a/src/main/java/org/traccar/speedlimit/SpeedLimitProvider.java b/src/main/java/org/traccar/speedlimit/SpeedLimitProvider.java new file mode 100644 index 000000000..f6579f87f --- /dev/null +++ b/src/main/java/org/traccar/speedlimit/SpeedLimitProvider.java @@ -0,0 +1,30 @@ +/* + * Copyright 2020 Anton Tananaev (anton@traccar.org) + * + * 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.speedlimit; + +public interface SpeedLimitProvider { + + interface SpeedLimitProviderCallback { + + void onSuccess(double speedLimit); + + void onFailure(Throwable e); + + } + + void getSpeedLimit(double latitude, double longitude, SpeedLimitProviderCallback callback); + +} diff --git a/src/main/java/org/traccar/web/CsvBuilder.java b/src/main/java/org/traccar/web/CsvBuilder.java index 3fe7e408f..b962be072 100644 --- a/src/main/java/org/traccar/web/CsvBuilder.java +++ b/src/main/java/org/traccar/web/CsvBuilder.java @@ -21,7 +21,6 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; -import java.util.Comparator; import java.util.Date; import java.util.Map; import java.util.SortedSet; @@ -45,23 +44,21 @@ public class CsvBuilder { private void addLineEnding() { builder.append(LINE_ENDING); } + private void addSeparator() { builder.append(SEPARATOR); } private SortedSet<Method> getSortedMethods(Object object) { Method[] methodArray = object.getClass().getMethods(); - SortedSet<Method> methods = new TreeSet<>(new Comparator<Method>() { - @Override - public int compare(Method m1, Method m2) { - if (m1.getName().equals("getAttributes") && !m1.getName().equals(m2.getName())) { - return 1; - } - if (m2.getName().equals("getAttributes") && !m1.getName().equals(m2.getName())) { - return -1; - } - return m1.getName().compareTo(m2.getName()); + SortedSet<Method> methods = new TreeSet<>((m1, m2) -> { + if (m1.getName().equals("getAttributes") && !m1.getName().equals(m2.getName())) { + return 1; + } + if (m2.getName().equals("getAttributes") && !m1.getName().equals(m2.getName())) { + return -1; } + return m1.getName().compareTo(m2.getName()); }); methods.addAll(Arrays.asList(methodArray)); return methods; diff --git a/src/main/java/org/traccar/web/WebServer.java b/src/main/java/org/traccar/web/WebServer.java index 12fa80d10..44d78cd27 100644 --- a/src/main/java/org/traccar/web/WebServer.java +++ b/src/main/java/org/traccar/web/WebServer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org) * * 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.web; +import org.eclipse.jetty.http.HttpCookie; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.proxy.AsyncProxyServlet; @@ -32,6 +33,7 @@ import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.servlet.ServletContainer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.traccar.api.DateParameterConverterProvider; import org.traccar.config.Config; import org.traccar.api.AsyncSocketServlet; import org.traccar.api.CorsResponseFilter; @@ -44,6 +46,7 @@ import org.traccar.config.Keys; import javax.servlet.DispatcherType; import javax.servlet.ServletException; +import javax.servlet.SessionCookieConfig; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; @@ -75,12 +78,8 @@ public class WebServer { ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS); - int sessionTimeout = config.getInteger("web.sessionTimeout"); - if (sessionTimeout > 0) { - servletHandler.getSessionHandler().setMaxInactiveInterval(sessionTimeout); - } - initApi(config, servletHandler); + initSessionConfig(config, servletHandler); if (config.getBoolean("web.console")) { servletHandler.addServlet(new ServletHolder(new ConsoleServlet()), "/console/*"); @@ -161,12 +160,39 @@ public class WebServer { } ResourceConfig resourceConfig = new ResourceConfig(); - resourceConfig.registerClasses(JacksonFeature.class, ObjectMapperProvider.class, ResourceErrorHandler.class); - resourceConfig.registerClasses(SecurityRequestFilter.class, CorsResponseFilter.class); + resourceConfig.registerClasses( + JacksonFeature.class, ObjectMapperProvider.class, ResourceErrorHandler.class, + SecurityRequestFilter.class, CorsResponseFilter.class, DateParameterConverterProvider.class); resourceConfig.packages(ServerResource.class.getPackage().getName()); servletHandler.addServlet(new ServletHolder(new ServletContainer(resourceConfig)), "/api/*"); } + private void initSessionConfig(Config config, ServletContextHandler servletHandler) { + int sessionTimeout = config.getInteger("web.sessionTimeout"); + if (sessionTimeout > 0) { + servletHandler.getSessionHandler().setMaxInactiveInterval(sessionTimeout); + } + + String sameSiteCookie = config.getString(Keys.WEB_SAME_SITE_COOKIE); + if (sameSiteCookie != null) { + SessionCookieConfig sessionCookieConfig = servletHandler.getServletContext().getSessionCookieConfig(); + switch (sameSiteCookie.toLowerCase()) { + case "lax": + sessionCookieConfig.setComment(HttpCookie.SAME_SITE_LAX_COMMENT); + break; + case "strict": + sessionCookieConfig.setComment(HttpCookie.SAME_SITE_STRICT_COMMENT); + break; + case "none": + sessionCookieConfig.setSecure(true); + sessionCookieConfig.setComment(HttpCookie.SAME_SITE_NONE_COMMENT); + break; + default: + break; + } + } + } + public void start() { try { server.start(); diff --git a/src/main/proto/StarLinkMessage.proto b/src/main/proto/StarLinkMessage.proto new file mode 100644 index 000000000..167b9f283 --- /dev/null +++ b/src/main/proto/StarLinkMessage.proto @@ -0,0 +1,330 @@ +syntax = "proto2"; + +package org.traccar.protobuf; + +// StarLink Message +message StarLink_Message { + optional uint32 FID = 1; // Unit factory serial + optional string IMEI = 2; // IMEI + optional uint32 UID = 3; // Unit serial + optional uint32 AckRef = 4; // Ack reference number + + repeated mMessage Message = 5; +} + +// Message body +message mMessage { + optional uint64 ReferenceNumber = 100; + optional uint32 ReferenceNumberDiff = 101; + optional uint32 MessageType = 102; + + optional mWakeUp WakeUp = 1; + optional mConfirmation Confirmation = 2; + optional mParamResponse ParamResponse = 3; + optional mEventReport EventReport = 6; + optional mGeoZone GeoZone = 10; + optional mOutputs Outputs = 12; + optional mEventConfig EventConfig = 14; + optional mInputs Inputs = 16; + optional mInputConfig InputConfig = 17; + optional mSpeedLimit SpeedLimit = 27; + optional mUnitIDChanged UnitIDChanged = 29; + optional mGenDeviceMsg GenericDeviceMsg= 30; + optional mGenDeviceFW GenericDeviceFW = 35; + optional mReadIButton ReadIButton = 42; + optional mGetPattern GetPattern = 43; + optional mMultiParam MultiParameter = 45; + optional mTempConfig TempConfig = 52; + optional mDeviceList DeviceList = 56; + optional mTeachRemote TeachRemote = 58; + optional mAuthenticate Authenticate = 95; + optional mEventReport CurrentLocation = 96; + optional mConfigServer ConfigServer = 98; +} + +// Wake-Up event +message mWakeUp { + required string UnitVersion = 1; + required string ProtocolVersion = 2; +} + +// Confirmation message +message mConfirmation { + // standard response + required uint32 ResultCode = 1; + optional uint32 ReferenceNumber = 2; // confirmation by reference number + optional uint32 MessageType = 3; // confirmation by message type + + // parameter ack (response to command #4) + optional uint32 ParameterAck = 4; + + // multi-parameter ack (response to command #46) + optional uint32 ParametersChanged = 5; + optional uint32 ParametersFailed = 6; + repeated uint32 ParameterFailedList = 9; + + // modify list of allowed drivers (response to command #50) + optional uint32 CodesInList = 7; + optional uint32 EmptySlots = 8; +} + +// Parameter response +message mParamResponse { + required uint32 ParameterNumber = 1; + required string Value = 2; +} + +// Event report +message mEventReport { + optional uint32 EID = 1; // https://sweb.erm.co.il/protocol/?i=D12 + optional string EDESC = 2; // https://sweb.erm.co.il/protocol/?i=D13 + optional uint32 EDT = 3; // https://sweb.erm.co.il/protocol/?i=D27 (seconds since 1/1/2000) + optional sint32 EDT_Diff = 4; // seconds since previous message's EDT + optional uint32 NXT = 5; // https://sweb.erm.co.il/protocol/?i=D111 + optional uint32 NXTS = 81; // https://sweb.erm.co.il/protocol/?i=D178 + optional string ExtraData = 254; + + optional uint32 PDT = 6; // https://sweb.erm.co.il/protocol/?i=D35 (seconds since 1/1/2000) + optional sint32 PDT_Diff = 7; // seconds since previous message's PDT + optional sint32 LAT = 8; // https://sweb.erm.co.il/protocol/?i=D49 (multiplied by 1000000) + optional sint32 LAT_Diff = 9; // difference between previous latitude (add this value to previous value) + optional sint32 LONG = 10; // https://sweb.erm.co.il/protocol/?i=D57 (multiplied by 1000000) + optional sint32 LONG_Diff = 11; // difference between previous longitude (add this value to previous value) + optional sint32 ALT = 12; // https://sweb.erm.co.il/protocol/?i=D69 (multiplied by 10) + optional sint32 ALT_Diff = 13; // difference between previous altitude (add this value to previous value) + optional uint32 SPD = 14; // https://sweb.erm.co.il/protocol/?i=D61 (multiplied by 10, in knots) + optional uint32 SPDK = 15; // https://sweb.erm.co.il/protocol/?i=D62 + optional uint32 HEAD = 16; // https://sweb.erm.co.il/protocol/?i=D63 + optional uint32 HDOP = 17; // https://sweb.erm.co.il/protocol/?i=D71 (multiplied by 10) + optional uint32 VDOP = 18; // https://sweb.erm.co.il/protocol/?i=D72 (multiplied by 10) + optional uint32 PDOP = 19; // https://sweb.erm.co.il/protocol/?i=D70 (multiplied by 10) + optional uint32 SAT = 20; // https://sweb.erm.co.il/protocol/?i=D73 + optional uint32 SATN = 21; // https://sweb.erm.co.il/protocol/?i=D99 + optional uint32 SATU = 22; // https://sweb.erm.co.il/protocol/?i=D74 + optional uint32 GSS = 23; // https://sweb.erm.co.il/protocol/?i=D98 + optional uint32 GSSN = 24; // https://sweb.erm.co.il/protocol/?i=D100 + optional uint32 FIX = 25; // https://sweb.erm.co.il/protocol/?i=D75 + optional uint32 LOCA = 26; // https://sweb.erm.co.il/protocol/?i=D126 + optional uint32 LOCS = 79; // https://sweb.erm.co.il/protocol/?i=D167 + + optional uint32 ODO = 27; // https://sweb.erm.co.il/protocol/?i=D14 + optional sint32 ODO_Diff = 28; // difference between previous odometer (add this value to previous value) + optional uint32 ODOD = 29; // https://sweb.erm.co.il/protocol/?i=D15 (multiplied by 1000) + optional sint32 ODOD_Diff = 30; // difference between previous odometer (add this value to previous value) + + optional uint32 CID = 31; // https://sweb.erm.co.il/protocol/?i=D5 + optional uint32 LAC = 32; // https://sweb.erm.co.il/protocol/?i=D4 + optional uint32 CSS = 33; // https://sweb.erm.co.il/protocol/?i=D89 + optional sint32 CSS_Diff = 34; // difference between previous rx power (add this value to previous value) + optional uint32 CTA = 35; // https://sweb.erm.co.il/protocol/?i=D95 + optional uint32 CCN = 36; // https://sweb.erm.co.il/protocol/?i=D89 + optional bool JAM = 37; // https://sweb.erm.co.il/protocol/?i=D66 + optional uint32 NC = 38; // https://sweb.erm.co.il/protocol/?i=D94 + optional uint32 NM = 39; // https://sweb.erm.co.il/protocol/?i=D125 + optional uint32 NT = 76; // https://sweb.erm.co.il/protocol/?i=D153 + optional uint32 CU = 77; // https://sweb.erm.co.il/protocol/?i=D154 + + optional uint32 VIN = 40; // https://sweb.erm.co.il/protocol/?i=D64 (multiplied by 100) + optional sint32 VIN_Diff = 41; // difference between previous VIN (add this value to previous value) + optional uint32 VBAT = 42; // https://sweb.erm.co.il/protocol/?i=D65 (multiplied by 100) + optional sint32 VBAT_Diff = 43; // difference between previous VBAT (add this value to previous value) + optional uint32 V3 = 44; // https://sweb.erm.co.il/protocol/?i=D92 (multiplied by 100) + optional sint32 V3_Diff = 45; // difference between previous V3 (add this value to previous value) + optional uint32 V4 = 46; // https://sweb.erm.co.il/protocol/?i=D93 (multiplied by 100) + optional sint32 V4_Diff = 47; // difference between previous V4 (add this value to previous value) + optional uint32 BATH = 48; + optional uint32 BATC = 49; + optional uint32 STRT = 50; // (multiplied by 100) + optional sint32 TVI = 51; // https://sweb.erm.co.il/protocol/?i=D110 (multiplied by 10) + + optional uint32 Inputs = 52; // b0 = IN1/INC, b1 = IN2/IND, b2 = IN3/INE, b3 = IN4/INF + optional uint32 DInputs = 53; // b0 = DI1, b1 = DI2, b2 = DI3, b3 = DI4, b4 = DI5, b5 = DI6, b6 = DI7, b7 = DI8 + optional uint32 IgnitionEngine = 54; // b0 = IGN, b1 = ENG, b2 = DRV + optional uint32 Outputs = 55; // b0 = OUT1/OUTA, b1 = OUT2/OUTB, b2 = OUT3/OUTC, b3 = OUT4/OUTD, b4 = OUT5, b5 = OUT6, b6 = OUT7, b7 = OUT8 + + optional string USER = 56; // https://sweb.erm.co.il/protocol/?i=D97 + optional string EDV1 = 57; // https://sweb.erm.co.il/protocol/?i=D179 + optional string EDV2 = 58; // https://sweb.erm.co.il/protocol/?i=D180 + optional string EDV3 = 59; // https://sweb.erm.co.il/protocol/?i=D181 + optional string EDV4 = 60; // https://sweb.erm.co.il/protocol/?i=D182 + + optional sint32 CV1 = 61; // https://sweb.erm.co.il/protocol/?i=D121 + optional sint32 CV2 = 62; // https://sweb.erm.co.il/protocol/?i=D122 + optional sint32 CV3 = 63; // https://sweb.erm.co.il/protocol/?i=D123 + optional sint32 CV4 = 64; // https://sweb.erm.co.il/protocol/?i=D124 + optional sint32 CV5 = 82; // https://sweb.erm.co.il/protocol/?i=D174 + optional sint32 CV6 = 83; // https://sweb.erm.co.il/protocol/?i=D175 + optional sint32 CV7 = 84; // https://sweb.erm.co.il/protocol/?i=D176 + optional sint32 CV8 = 85; // https://sweb.erm.co.il/protocol/?i=D177 + optional sint32 CV9 = 86; // https://sweb.erm.co.il/protocol/?i=D188 + optional sint32 CV10 = 87; // https://sweb.erm.co.il/protocol/?i=D189 + optional sint32 CV11 = 88; // https://sweb.erm.co.il/protocol/?i=D190 + optional sint32 CV12 = 89; // https://sweb.erm.co.il/protocol/?i=D191 + + optional bool IARM = 65; // https://sweb.erm.co.il/protocol/?i=D7 + optional string DID = 66; // https://sweb.erm.co.il/protocol/?i=D101 + optional string DAL = 67; // https://sweb.erm.co.il/protocol/?i=D76 + optional uint32 DEST = 68; // https://sweb.erm.co.il/protocol/?i=D8 + optional bool OVM = 73; // https://sweb.erm.co.il/protocol/?i=D150 + + optional uint32 CFL = 69; // https://sweb.erm.co.il/protocol/?i=D26 + optional uint32 CFL2 = 80; // https://sweb.erm.co.il/protocol/?i=D26 + optional uint32 RPM = 70; // https://sweb.erm.co.il/protocol/?i=D144 + + optional uint32 DUR = 71; // https://sweb.erm.co.il/protocol/?i=D145 + optional uint32 TDUR = 72; // https://sweb.erm.co.il/protocol/?i=D146 + optional uint32 IDL = 74; // https://sweb.erm.co.il/protocol/?i=D151 + optional uint32 STP = 75; // https://sweb.erm.co.il/protocol/?i=D152 + + repeated mEventReport_TDx TDx = 78; // https://sweb.erm.co.il/protocol/?i=D168 +} + +// Sensor data +message mEventReport_TDx { + required uint32 SensorNumber = 1; // Sensor # + optional string SensorID = 3; // TIx (binary) + + optional sint32 Temperature = 4; // TVx (multiplied by 10) + optional uint32 Temperature_State = 2; // TSx + + optional uint32 Humidity = 5; // THx (multiplied by 10) + optional uint32 Humidity_State = 6; + + optional uint32 RSSI = 7; + optional uint32 RSSI_State = 8; + + optional uint32 Voltage = 9; // mV (multiplied by 1000) + optional uint32 Voltage_State = 10; + + optional uint32 Light = 11; // percent + optional uint32 Light_State = 12; + + optional uint32 MagnetSwitch = 13; + optional uint32 MagnetSwitch_State = 14; + + optional uint32 Accident = 15; + optional uint32 Button = 16; + optional uint32 Movement = 17; +} + +// Geozone reqsponse +message mGeoZone { + required uint32 ZoneNumber = 1; + repeated mLatLong Point = 2; // single point for radius geozone, multiple points for polygon + optional uint32 Radius = 3; +} + +// Outputs response +message mOutputs { + required uint32 OutputBitmask = 1; +} + +// Event configuration response +message mEventConfig { + required uint32 EventID = 1; + required uint32 Configuration = 2; // bitmask +} + +// Inputs response +message mInputs { + required uint32 InputBitmask = 1; +} + +// Inputs configuration +message mInputConfig { + required uint32 InputID = 1; + required uint32 DebounceLow = 2; + required uint32 DebounceHigh = 3; +} + +// Speed limit configuration +message mSpeedLimit { + required uint32 Number = 1; + required bool Active = 2; + optional uint32 MinSpeed = 3; // when using speed limit with minimum speed + optional uint32 MaxSpeed = 4; // when using speed limit with maximum speed + required uint32 Duration = 5; + required bool ReportDuration = 6; + optional bool StartAndEnd = 7; + optional bool AutoTrack = 8; + optional bool OutputPattern = 9; + optional uint32 Step = 10; +} + +// Unit ID changed +message mUnitIDChanged { + required uint32 FID = 1; + required string IMEI = 2; + required sint32 PreviousSetting = 3; + required sint32 NewSetting = 4; +} + +// Generic device message +message mGenDeviceMsg { + required uint32 DeviceID = 1; + required uint32 DataType = 2; + optional string Data = 3; +} + +// Generic device FW update +message mGenDeviceFW { + optional uint32 BlockRequest = 1; + optional uint32 Status = 2; + optional uint32 MaxBlocks = 3; +} + +// Configuration server message +message mConfigServer { + required bool Success = 1; +} + +// Read iButton +message mReadIButton { + required string iButtonID = 1; + optional string Data = 2; +} + +// Read pattern configuration +message mGetPattern { + required uint32 PatternID = 1; + required string Pattern = 2; +} + +// Multiple parameters response +message mMultiParam { + repeated uint32 ParameterNumbers = 1; + repeated string Values = 2; +} + +// Temperature sensor configuration +message mTempConfig { + required uint32 SensorCount = 1; + repeated string SensorID = 2; +} + +// Device list +message mDeviceList { + repeated uint32 DeviceID = 1; + repeated uint32 Version = 2; +} + +// Teach remote +message mTeachRemote { + required uint32 RuleID = 1; + optional uint32 RemoteID = 2; +} + +// Authentication message +message mAuthenticate { + required uint32 Version = 1; + optional string Token = 2; + optional uint32 Key = 3; + optional uint32 Step = 4; + optional string Digest = 5; +} + +// Point (Lat / Long) +message mLatLong { + required sint32 Latitude = 1; + required sint32 Longitude = 2; +} diff --git a/src/test/java/org/traccar/ProtocolTest.java b/src/test/java/org/traccar/ProtocolTest.java index bfff4eef3..56551f247 100644 --- a/src/test/java/org/traccar/ProtocolTest.java +++ b/src/test/java/org/traccar/ProtocolTest.java @@ -205,6 +205,8 @@ public class ProtocolTest extends BaseTest { assertNotNull("protocol is null", position.getProtocol()); + assertTrue("deviceId > 0", position.getDeviceId() > 0); + } Map<String, Object> attributes = position.getAttributes(); diff --git a/src/test/java/org/traccar/TestIdentityManager.java b/src/test/java/org/traccar/TestIdentityManager.java index af5dd22df..7d2865e74 100644 --- a/src/test/java/org/traccar/TestIdentityManager.java +++ b/src/test/java/org/traccar/TestIdentityManager.java @@ -53,7 +53,10 @@ public final class TestIdentityManager implements IdentityManager { @Override public String lookupAttributeString( long deviceId, String attributeName, String defaultValue, boolean lookupServer, boolean lookupConfig) { - return "alarm,result"; + if (attributeName.equals("filter.skipAttributes")) { + return "alarm,result"; + } + return defaultValue; } @Override diff --git a/src/test/java/org/traccar/helper/ChecksumTest.java b/src/test/java/org/traccar/helper/ChecksumTest.java index ff48527bc..248f4dcae 100644 --- a/src/test/java/org/traccar/helper/ChecksumTest.java +++ b/src/test/java/org/traccar/helper/ChecksumTest.java @@ -42,8 +42,11 @@ public class ChecksumTest { assertEquals(0x00, Checksum.modulo256(ByteBuffer.wrap(new byte[] {0x00}))); assertEquals(0x00, Checksum.modulo256(ByteBuffer.wrap(new byte[] {0x00, 0x00, 0x00}))); assertEquals(0xca, Checksum.modulo256(ByteBuffer.wrap(new byte[] {0x77, 0x77, 0x77, 0x77, 0x77, 0x77}))); + } - + @Test + public void testNmea() { + assertEquals("*2A", Checksum.nmea("GSC,011412000010789,M4(Ro=500)")); } } diff --git a/src/test/java/org/traccar/helper/ObdDecoderTest.java b/src/test/java/org/traccar/helper/ObdDecoderTest.java index 1ffe68c8b..d5071bd51 100644 --- a/src/test/java/org/traccar/helper/ObdDecoderTest.java +++ b/src/test/java/org/traccar/helper/ObdDecoderTest.java @@ -9,11 +9,11 @@ public class ObdDecoderTest { @Test public void testDecode() { - assertEquals(83, ObdDecoder.decode(0x01, "057b").getValue()); - assertEquals(1225, ObdDecoder.decode(0x01, "0C1324").getValue()); - assertEquals(20, ObdDecoder.decode(0x01, "0D14").getValue()); - assertEquals(64050, ObdDecoder.decode(0x01, "31fa32").getValue()); - assertEquals(25, ObdDecoder.decode(0x01, "2F41").getValue()); + assertEquals(83L, ObdDecoder.decode(0x01, "057b").getValue()); + assertEquals(1225L, ObdDecoder.decode(0x01, "0C1324").getValue()); + assertEquals(20L, ObdDecoder.decode(0x01, "0D14").getValue()); + assertEquals(64050L, ObdDecoder.decode(0x01, "31fa32").getValue()); + assertEquals(25L, ObdDecoder.decode(0x01, "2F41").getValue()); } diff --git a/src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java b/src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java index 3a26bb7a7..bb86a9629 100644 --- a/src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java @@ -11,6 +11,10 @@ public class AtrackFrameDecoderTest extends ProtocolTest { AtrackFrameDecoder decoder = new AtrackFrameDecoder(); verifyFrame( + binary("4052698c032a924f000147027fe5d7425f642e56060f031847bb68cb500719e26752c25bebc11c7fddce2b8ed4eff4ed863b187cc6653b5b1c1fc6803884d21aeeedae2ec6e72781d97e95b965610c1d107e5400cd5a7b7b3b592e676091c6a5893d80af9b3c63ae4de20d6e5bc60440bf2c299fbabfe268039d558e4b8589dd5173c926b7f51b916ba29f21d46ff9170793fe450072d691896e114fddce4fd29f7f2f9b74e41ca83814015e8a00ffd1f9bd475e2a44624e074a009455ab5628e39fce8036a09368cf1d2ba0d2653b979c0a00e9edc82335a56d1ee6071401d468b0f4cd761a743d011401d15b4636015721870dd0500695b2edabeabf2f4a00a514645cc83a739ad165f320c1ed401617a0a2800a2803ffd2faa68a004660aa598e00acd8f4d866d54eab3c7994284881fe11ebf5e68034e8a0086e674850927f0ae2bc4dafa5844659674451d39e49a00e1dae23d76ed67bb72211d109e4d5bd756da3b68a4b755021e30076a00cf31431a064e41e6a19a68d5396518f7a00f1ff008bfe27f31068766dbb7e1a723d3fbbf8d79aeb764748b489662be7ccbbb6820e07d4500734caa727765aa32ac0720e28026b4bb9ed7ccf2594798bb5b2a0f1f8f4a82800a2803fffd3f9b97352ae02e45004c808e4f7a997823bd005e86600618f26b7b4a9cab819fa500767a749b9403cd74162b903de803acd1e3c28aebf4d4c81401d05a4441fad682444738a00b712f2055f03e502802b14c5dffbc2ac106343ed4012a905411e94b40051401fffd4faa6992488980c793d0773400d54676df2f1e8b9e054b400564ebb77750c463b442d2119247f08f53e9401e7da85d6a12cd221d427217a856c60fe15caea9689292f2832bfac8777f3a00e67538ef150ff00665d9b4b95fba4aee46f623fa8ae26fbe24f88b49b87d3b5bd2a12e38ca3950e3d41e7228008be2ac02d423dadc09071c1047e791fcab96d77c79acdf92969279113f1c1cb7e7401876c4c939b8ba73230e5998e49ac4d66ee6bebd796462ddb9f4ed40140a12339e9dea225b1824d0025140051401fffd5f9bc676f6a7ae4af6e280255cf5c7153a7b0a0052d8715bba64bf32f39a00ed74694902bb1d306e65c500763a5afca2baed2"), + decoder.decode(null, null, binary("4052698c032a924f000147027fe5d7425f642e56060f031847bb68cb500719e26752c25bebc11c7fddce2b8ed4eff4ed863b187cc6653b5b1c1fc6803884d21aeeedae2ec6e72781d97e95b965610c1d107e5400cd5a7b7b3b592e676091c6a5893d80af9b3c63ae4de20d6e5bc60440bf2c299fbabfe268039d558e4b8589dd5173c926b7f51b916ba29f21d46ff9170793fe450072d691896e114fddce4fd29f7f2f9b74e41ca83814015e8a00ffd1f9bd475e2a44624e074a009455ab5628e39fce8036a09368cf1d2ba0d2653b979c0a00e9edc82335a56d1ee6071401d468b0f4cd761a743d011401d15b4636015721870dd0500695b2edabeabf2f4a00a514645cc83a739ad165f320c1ed401617a0a2800a2803ffd2faa68a004660aa598e00acd8f4d866d54eab3c7994284881fe11ebf5e68034e8a0086e674850927f0ae2bc4dafa5844659674451d39e49a00e1dae23d76ed67bb72211d109e4d5bd756da3b68a4b755021e30076a00cf31431a064e41e6a19a68d5396518f7a00f1ff008bfe27f31068766dbb7e1a723d3fbbf8d79aeb764748b489662be7ccbbb6820e07d4500734caa727765aa32ac0720e28026b4bb9ed7ccf2594798bb5b2a0f1f8f4a82800a2803fffd3f9b97352ae02e45004c808e4f7a997823bd005e86600618f26b7b4a9cab819fa500767a749b9403cd74162b903de803acd1e3c28aebf4d4c81401d05a4441fad682444738a00b712f2055f03e502802b14c5dffbc2ac106343ed4012a905411e94b40051401fffd4faa6992488980c793d0773400d54676df2f1e8b9e054b400564ebb77750c463b442d2119247f08f53e9401e7da85d6a12cd221d427217a856c60fe15caea9689292f2832bfac8777f3a00e67538ef150ff00665d9b4b95fba4aee46f623fa8ae26fbe24f88b49b87d3b5bd2a12e38ca3950e3d41e7228008be2ac02d423dadc09071c1047e791fcab96d77c79acdf92969279113f1c1cb7e7401876c4c939b8ba73230e5998e49ac4d66ee6bebd796462ddb9f4ed40140a12339e9dea225b1824d0025140051401fffd5f9bc676f6a7ae4af6e280255cf5c7153a7b0a0052d8715bba64bf32f39a00ed74694902bb1d306e65c500763a5afca2baed2"))); + + verifyFrame( binary("40502c414246342c3532362c3939312c3335363936313037353933313136352c313533343338313635362c313533343338313635392c313533343338313635392c2d38383432393138382c34343237313232352c37302c322c3230303536332c392c312c302c302c302c2c323030302c323030302c1a2c25434925434525434e25475125475325464c254d4c25564e25504425464325454c254554254344254154254d46254d5625425625434d25445425474c25474e254756254c43254d4525524c25525025534125534d255452254941254d502c302c3331303236302c31382c392c302c302c3254314b523332453238433730363138352c302c302c302c35342c383930313236303838313231353234373735392c3235322c36302c3132322c34312c3331303236303838313532343737352c302c687474703a2f2f6d6170732e676f6f676c652e636f6d2f6d6170733f713d34342e3237313232352c2d38382e343239313834201a2c3030393830303442303346343030393830303442303346343030393830303442303346333030393830303442303346343030393830303442303346343030393930303442303346343030393830303442303346343030393830303442303346343030393830303442303346343030393830303442303346331a2c3030343846463130303345381a2c302c3335363936313037353933313136352c302c3938302c31322c302c31382c35302c300d0a"), decoder.decode(null, null, binary("40502c414246342c3532362c3939312c3335363936313037353933313136352c313533343338313635362c313533343338313635392c313533343338313635392c2d38383432393138382c34343237313232352c37302c322c3230303536332c392c312c302c302c302c2c323030302c323030302c1a2c25434925434525434e25475125475325464c254d4c25564e25504425464325454c254554254344254154254d46254d5625425625434d25445425474c25474e254756254c43254d4525524c25525025534125534d255452254941254d502c302c3331303236302c31382c392c302c302c3254314b523332453238433730363138352c302c302c302c35342c383930313236303838313231353234373735392c3235322c36302c3132322c34312c3331303236303838313532343737352c302c687474703a2f2f6d6170732e676f6f676c652e636f6d2f6d6170733f713d34342e3237313232352c2d38382e343239313834201a2c3030393830303442303346343030393830303442303346343030393830303442303346333030393830303442303346343030393830303442303346343030393930303442303346343030393830303442303346343030393830303442303346343030393830303442303346343030393830303442303346331a2c3030343846463130303345381a2c302c3335363936313037353933313136352c302c3938302c31322c302c31382c35302c300d0a"))); diff --git a/src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java index a604473b8..b329f59f3 100644 --- a/src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java @@ -10,6 +10,9 @@ public class AtrackProtocolDecoderTest extends ProtocolTest { AtrackProtocolDecoder decoder = new AtrackProtocolDecoder(null); + verifyNull(decoder, binary( + "4052698c032a924f000147027fe5d7425f642e56060f031847bb68cb500719e26752c25bebc11c7fddce2b8ed4eff4ed863b187cc6653b5b1c1fc6803884d21aeeedae2ec6e72781d97e95b965610c1d107e5400cd5a7b7b3b592e676091c6a5893d80af9b3c63ae4de20d6e5bc60440bf2c299fbabfe268039d558e4b8589dd5173c926b7f51b916ba29f21d46ff9170793fe450072d691896e114fddce4fd29f7f2f9b74e41ca83814015e8a00ffd1f9bd475e2a44624e074a009455ab5628e39fce8036a09368cf1d2ba0d2653b979c0a00e9edc82335a56d1ee6071401d468b0f4cd761a743d011401d15b4636015721870dd0500695b2edabeabf2f4a00a514645cc83a739ad165f320c1ed401617a0a2800a2803ffd2faa68a004660aa598e00acd8f4d866d54eab3c7994284881fe11ebf5e68034e8a0086e674850927f0ae2bc4dafa5844659674451d39e49a00e1dae23d76ed67bb72211d109e4d5bd756da3b68a4b755021e30076a00cf31431a064e41e6a19a68d5396518f7a00f1ff008bfe27f31068766dbb7e1a723d3fbbf8d79aeb764748b489662be7ccbbb6820e07d4500734caa727765aa32ac0720e28026b4bb9ed7ccf2594798bb5b2a0f1f8f4a82800a2803fffd3f9b97352ae02e45004c808e4f7a997823bd005e86600618f26b7b4a9cab819fa500767a749b9403cd74162b903de803acd1e3c28aebf4d4c81401d05a4441fad682444738a00b712f2055f03e502802b14c5dffbc2ac106343ed4012a905411e94b40051401fffd4faa6992488980c793d0773400d54676df2f1e8b9e054b400564ebb77750c463b442d2119247f08f53e9401e7da85d6a12cd221d427217a856c60fe15caea9689292f2832bfac8777f3a00e67538ef150ff00665d9b4b95fba4aee46f623fa8ae26fbe24f88b49b87d3b5bd2a12e38ca3950e3d41e7228008be2ac02d423dadc09071c1047e791fcab96d77c79acdf92969279113f1c1cb7e7401876c4c939b8ba73230e5998e49ac4d66ee6bebd796462ddb9f4ed40140a12339e9dea225b1824d0025140051401fffd5f9bc676f6a7ae4af6e280255cf5c7153a7b0a0052d8715bba64bf32f39a00ed74694902bb1d306e65c500763a5afca2baed2")); + verifyPositions(decoder, binary( "03012c433538312c3135372c342c3335383838373039353933353839342c32303230303430313037353933312c32303230303430313037353933312c32303230303430313037353933312c32373933393534312c2d32363132313934332c3238382c302c3136322c31312c302c302c302c302c2c323030302c323030302c1a2c313537352c302c302c302c3132342c302c31302c302c302c302c302c3132352c302c372c302c0d0a")); @@ -102,7 +105,7 @@ public class AtrackProtocolDecoderTest extends ProtocolTest { } - /*@Test + @Test public void testDecodeCustom() throws Exception { AtrackProtocolDecoder decoder = new AtrackProtocolDecoder(null); @@ -119,6 +122,6 @@ public class AtrackProtocolDecoderTest extends ProtocolTest { verifyPositions(decoder, buffer( "@P,DCCE,422,5818,357766091026083,1557904779,1557904780,1557904780,-121899644,37406291,129,2,21,10,0,0,0,0,,2000,2000,,13,40,8942310017000752067,21096194,295050910083206,310260,0,FF00001F0393FF01001E0395FF01001E0394FF01001F0393FF02001D0393FF00001F0394FF0100200394FF01001F0393FF02001F0395FF0100200394,20,10,002C005C03B4,14953,357766091026083,125,38,11,0,1,Device:Fail,JN8AZ1MU1BW066090,0,0,0,0,0,99,0,0,0,0,0,264,5,0,0,0,0,0,0,0,0\r\n")); - }*/ + } } diff --git a/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java index 24b419953..08b2641aa 100644 --- a/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java @@ -11,10 +11,17 @@ public class EelinkProtocolDecoderTest extends ProtocolTest { EelinkProtocolDecoder decoder = new EelinkProtocolDecoder(null); + verifyPositions(decoder, binary( + "454c029249a50354679090044671676712004321315f3cf43503fc94d3760c79328a0129000000000a01f9000190330905580d2e046f118a04ec00000000ccc7086c02fe000000000000000000000000000000000000676712004321325f3cf43e03fc94d3760c79328a0129000000000901f9000190330905580d2e046f117b04ec00000000ccc7086d02ff000000000000000000000000000000000000676712004321335f3cf44703fc94d3760c79328a0129000000000901f9000190330905580d2e046f117f04ec00000000ccc7086d02ff000000000000000000000000000000000000676712004321345f3cf45303fc94d3760c79328a0129000000000901f9000190330905580d2e046f119d04ec00000000ccc7086d02ff000000000000000000000000000000000000676712004321355f3cf45c03fc94d3760c79328a0129000000000801f9000190330905580d2e046f11a304ec00000000ccc7086d02ff000000000000000000000000000000000000676712004321365f3cf46603fc94d3760c79328a0129000000000801f9000190330905580d2e046f118804df00000000ccc7086d02ff000000000000000000000000000000000000676712004321375f3cf47103fc94d3760c79328a0129000000000901f9000190330905580d2e046f119704ec00000000ccc7086d02ff000000000000000000000000000000000000676712004321385f3cf47a03fc94d3760c79328a0129000000000901f9000190330905580d2e046f118204ec00000000ccc7086e0300000000000000000000000000000000000000676712004321395f3cf48303fc94d3760c79328a0129000000000901f9000190330905580d2e046f117604df00000000ccc7086e0300000000000000000000000000000000000000")); + + verifyAttribute(decoder, binary( + "676714001500035f74a2940201360104591100a7160122250400"), + Position.KEY_ALARM, Position.ALARM_REMOVING); + verifyNull(decoder, binary( "454C0027E753035254407167747167670100180002035254407167747100200205020500010432000086BD")); - verifyAttributes(decoder, binary( + verifyPositions(decoder, false, binary( "454C03EA2B040865284045749185676712013B001E5E8ED5A60000880ED8000000000000000000030003000000000000000000005C550000000000001102F1A00220F0FEA700B10C0BE91510001828D97ECC95D1C400B1030BFD16C00015B3B0FE237EC7C00001040BD816200019BDC691C032E6C000B1020C2015D00003C67480A034F7A30001040BDC1310001CE52429096FDCBA00B1020C1C15F0001F9AEB3CC9C8D49F0001040B7316200014C0A26B6495EFAB0001040ACC1A400013D6313E4CE3EED700B1030BF516800016BE49BD3E05EDB00001040BF01620001DDB15BDE209E4A800B10C0BE515300014BA13142CB0C7AA0001040BD815D00019D39722BB52DBA30001040BF317500011020C850118EFC50001020A8115F000188B659FD04CD1AD0001040BE816000014AC91F5C96BDBAC0001040BF214B0001495CDFA62B6D2AB00B10C0BDB15C00013676712014B001F5E8ED5AC0000880ED8000000000000000000030003000000000000000000005F8D0000000000001202B3B0FE237EC7C00001040BD816200019BDC691C032E6C000B1020C2015D00003C67480A034F7A30001040BDC1310001CE52429096FDCBA00B1020C1C15F0001FBE49BD3E05EDB00001040BF01620001DDB15BDE209E4A800B10C0BE515300014BA13142CB0C7AA0001040BD815D00019D39722BB52DBA30001040BF317500011020C850118EFC50001020A8115F000188B659FD04CD1AD0001040BE816000014AC91F5C96BDBAC0001040BF214B0001495CDFA62B6D2AB00B10C0BDB15C00013F1A00220F0FEA700B10C0BE9152000189AEB3CC9C8D4A30001040B731620001428D97ECC95D1C200B1030C0016C00015372CFB9E84C3A00029020F5100000000D6313E4CE3EED700B1030BF716800016C0A26B6495EFB10001040ACC1A400013676712014B00205E8ED5B10000880ED8000000000000000000030003000000000000000000005C550000000000001202B3B0FE237EC7C00001040BD816200019BDC691C032E6C000B1020C2015D00003DB15BDE209E4A800B10C0BE515300014BA13142CB0C7AA0001040BD815D00019D39722BB52DBA30001040BF317500011020C850118EFC50001020A8115F000188B659FD04CD1AD0001040BE81600001495CDFA62B6D2AB00B10C0BDB15C000139AEB3CC9C8D4A30001040B731620001428D97ECC95D1C200B1030C0016C00015372CFB9E84C3A00029020F5100000000C0A26B6495EFB10001040ACC1A400013BE49BD3E05EDA90001040BF01620001DAC91F5C96BDBA50001040BF014B00014F1A00220F0FE9F00B10C0BE915200018E52429096FDCC200B1020C1B15F0001FD6313E4CE3EED200B1030BF716800016C67480A034F7A40001040BDC1300001C")); verifyAttribute(decoder, binary( diff --git a/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java index 88a460854..48b74c904 100644 --- a/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java @@ -12,6 +12,9 @@ public class FifotrackProtocolDecoderTest extends ProtocolTest { FifotrackProtocolDecoder decoder = new FifotrackProtocolDecoder(null); verifyPosition(decoder, buffer( + "$$118,863003046473534,258,A01,,201007231735,V,3.067783,101.672858,0,176,96,189890,0,A0,03,0,502|19|5C1|93349F,196|4E0|6C,1,*13")); + + verifyPosition(decoder, buffer( "$$116,869270049149999,5,A01,4,190925080127,V,-15.804260,35.061506,0,0,1198,0,0,900000C0,02,0,650|10|12C|B24,18B|4C8|72,1,*01")); verifyAttribute(decoder, buffer( diff --git a/src/test/java/org/traccar/protocol/FutureWayFrameDecoderTest.java b/src/test/java/org/traccar/protocol/FutureWayFrameDecoderTest.java new file mode 100644 index 000000000..fb1e395f1 --- /dev/null +++ b/src/test/java/org/traccar/protocol/FutureWayFrameDecoderTest.java @@ -0,0 +1,19 @@ +package org.traccar.protocol; + +import org.junit.Test; +import org.traccar.ProtocolTest; + +public class FutureWayFrameDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + FutureWayFrameDecoder decoder = new FutureWayFrameDecoder(); + + verifyFrame( + binary("34313030303030303346323030303032302c494d45493a3335343832383130303132363436312c62617474657279206c6576656c3a362c6e6574776f726b20747970653a372c4353513a323336463432"), + decoder.decode(null, null, binary("34313030303030303346323030303032302c494d45493a3335343832383130303132363436312c62617474657279206c6576656c3a362c6e6574776f726b20747970653a372c4353513a323336463432"))); + + } + +} diff --git a/src/test/java/org/traccar/protocol/FutureWayProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FutureWayProtocolDecoderTest.java new file mode 100644 index 000000000..951b3bb20 --- /dev/null +++ b/src/test/java/org/traccar/protocol/FutureWayProtocolDecoderTest.java @@ -0,0 +1,21 @@ +package org.traccar.protocol; + +import org.junit.Test; +import org.traccar.ProtocolTest; + +public class FutureWayProtocolDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + FutureWayProtocolDecoder decoder = new FutureWayProtocolDecoder(null); + + verifyNull(decoder, text( + "410000003F2000020,IMEI:354828100126461,battery level:6,network type:7,CSQ:236F42")); + + verifyPosition(decoder, text( + "410000009BA00004GPS:V,200902093333,0.000000N,0.000000E,0.000,0.000\r\nWIFI:3,1|90-67-1C-F7-21-6C|52&2|80-89-17-C6-79-A0|54&3|40-F4-20-EF-DD-2A|58\r\nLBS:460,0,46475066,69\r\n6A42")); + + } + +} diff --git a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java index 12e64cd6d..915876609 100644 --- a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java @@ -11,6 +11,22 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest { Gl200TextProtocolDecoder decoder = new Gl200TextProtocolDecoder(null); + verifyAttribute(decoder, buffer( + "+RESP:GTERI,DE0115,865284042104863,gl500m,00000100,0,0,1,2,0.0,0,36.9,-1.844589,52.177779,20201006125701,0234,0015,0135,34A1,19,0,,79,1,,0,20201006125723,184D$"), + Position.KEY_BATTERY_LEVEL, 79); + + verifyAttribute(decoder, buffer( + "+RESP:GTFRI,DE0114,865284042140479,,0,0,1,1,0.0,0,28.0,-118.268093,33.975430,20200901105954,0311,0480,3500,00D07F02,18,0,,93,0,,,20200901110000,0355$"), + Position.KEY_BATTERY_LEVEL, 93); + + verifyAttribute(decoder, buffer( + "+RESP:GTFRI,DE0114,865284041308986,,0,0,1,0,0.0,0,245.6,-117.678624,34.032081,20200825030332,0311,0480,3304,00C7F601,24,0,,85,1,1,,20200825030738,03B1$"), + Position.KEY_BATTERY_LEVEL, 85); + + verifyAttribute(decoder, buffer( + "+RESP:GTFRI,380903,869606020188383,,,40,1,2,43.4,80,252.4,8.606297,50.700200,20200721090109,0262,0001,1932,1BA4,00,0.0,,,,0,220100,,,,20200721090110,00B9$"), + Position.PREFIX_IN + 1, false); + verifyPositions(decoder, buffer( "+RESP:GTFRI,423031,355154083021002,Bolt4G,0,0,0,0,1,1.0,0.2,0,245.3,-85.630193,42.975280,20190729185934,310,410,500b,B0E320F,31,-1,100,20190729185934,0010$")); diff --git a/src/test/java/org/traccar/protocol/GlobalSatProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/GlobalSatProtocolEncoderTest.java new file mode 100644 index 000000000..8cfa9299b --- /dev/null +++ b/src/test/java/org/traccar/protocol/GlobalSatProtocolEncoderTest.java @@ -0,0 +1,39 @@ +package org.traccar.protocol; + +import org.junit.Test; +import org.traccar.ProtocolTest; +import org.traccar.model.Command; + +import static org.junit.Assert.assertEquals; + +public class GlobalSatProtocolEncoderTest extends ProtocolTest { + + @Test + public void testEncodeAlarmDismiss() { + + GlobalSatProtocolEncoder encoder = new GlobalSatProtocolEncoder(null); + + Command command = new Command(); + command.setDeviceId(1); + command.setType(Command.TYPE_ALARM_DISMISS); + + assertEquals("GSC,123456789012345,Na*48!", encoder.encodeCommand(command)); + + } + + @Test + public void testEncodeOutputControl() { + + GlobalSatProtocolEncoder encoder = new GlobalSatProtocolEncoder(null); + + Command command = new Command(); + command.setDeviceId(1); + command.setType(Command.TYPE_OUTPUT_CONTROL); + command.set(Command.KEY_INDEX, 1); + command.set(Command.KEY_DATA, "1"); + + assertEquals("GSC,123456789012345,Lo(1,1)*69!", encoder.encodeCommand(command)); + + } + +} diff --git a/src/test/java/org/traccar/protocol/GlobalstarProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GlobalstarProtocolDecoderTest.java index f7bf8f514..75d30ec47 100644 --- a/src/test/java/org/traccar/protocol/GlobalstarProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GlobalstarProtocolDecoderTest.java @@ -11,6 +11,17 @@ public class GlobalstarProtocolDecoderTest extends ProtocolTest { GlobalstarProtocolDecoder decoder = new GlobalstarProtocolDecoder(null); + verifyNull(decoder, request(HttpMethod.POST, "/", buffer( + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", + "<stuMessages xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"http://cody.glpconnect.com/XSD/StuMessage_Rev1_0_1.xsd\" timeStamp=\"16/09/2020 01:33:07 GMT\" messageID=\"567207180ae9100687cef8c81978371a\">\n", + "<stuMessage>\n", + "<esn>0-4325340</esn>\n", + "<unixTime>1600220003</unixTime>\n", + "<gps>N</gps>\n", + "<payload length=\"9\" source=\"pc\" encoding=\"hex\">0x63FFFF1BB4FFFFFFFF</payload>\n", + "</stuMessage>\n", + "</stuMessages>"))); + verifyPositions(decoder, request(HttpMethod.POST, "/", buffer( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "<stuMessages xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"http://cody.glpconnect.com/XSD/StuMessage_Rev1_0_1.xsd\" timeStamp=\"25/03/2020 03:02:32 GMT\" messageID=\"300421a0fd2a100585bdde409d6f601a\">", diff --git a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java index 25d6870bc..e6b7b7ce3 100644 --- a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java @@ -18,6 +18,13 @@ public class Gt06ProtocolDecoderTest extends ProtocolTest { "78780D01086471700328358100093F040D0A")); verifyAttribute(decoder, binary( + "7979000E9B0332382E33A1E60D0A0289BE490D0A"), + Position.PREFIX_TEMP + 1, 28.3); + + verifyPosition(decoder, binary( + "7878353714080d05000ac500a886eb0b7522f000100001fe0a05ea004f1b000001002e0400002328003b0217c0003c0401020001002c468a0d0a")); + + verifyAttribute(decoder, binary( "79790020940a035985708236675805200502187214018966051912408052452f000355560d0a"), Position.KEY_ICCID, "8966051912408052452"); diff --git a/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java index b624f69ab..9ba31e08a 100644 --- a/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java @@ -2,6 +2,7 @@ package org.traccar.protocol; import org.junit.Test; import org.traccar.ProtocolTest; +import org.traccar.model.Position; public class HuaShengProtocolDecoderTest extends ProtocolTest { @@ -16,6 +17,10 @@ public class HuaShengProtocolDecoderTest extends ProtocolTest { verifyNull(decoder, binary( "c000000077aa0200000000000e000100143347315f48312e315f56312e30372e54000300133335353835353035303434303635380004000b3531323030303000050005010006000400070004000800050000090018383936313032353431343533333239313833360d000a000f796573696e7465726e6574c0")); + verifyAttribute(decoder, binary( + "c000000049aa0000000000028e8800000032303038323630373534323800e1d47fffcd163d0000000000f30000000100157703f8000046000000000aade0ffffffff0011000800000496c0"), + Position.KEY_HOURS, 58.7); + verifyNotNull(decoder, binary( "c000000077aa00000000000070020000003230303132373035313635330000000000000000000000000000000000010015ffffffff000000000000019dffffffffff0005000a1f00000e455a00200019313238354031406666666540386233663930634030000f0013333536373236313038313335343530c0")); diff --git a/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java index f600a77d4..f44accc57 100644 --- a/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java @@ -15,6 +15,10 @@ public class HuabaoProtocolDecoderTest extends ProtocolTest { "7E01000021013345678906000F002C012F373031313142534A2D4D3742203030303030303001D4C1423838383838B47E")); verifyAttribute(decoder, binary( + "7e070400db07904116875014480003010046000000000000000b020057d40072fb9c0064017200f220090215230301040000092930011f31010ceb1c000c00b28921200272401241734f00060089ffffffef000400ce1d460046000000000000000b0200570c0072fdb2005f013600ee20090215230801040000092a30011f31010ceb1c000c00b28921200272401241734f00060089ffffffef000400ce1e800046000000000000000b020056ac0072fe9b005a00d200ec20090215231301040000092a30011b31010ceb1c000c00b28921200272401241734f00060089ffffffef000400ce1e35d77e"), + Position.KEY_POWER, 74.94); + + verifyAttribute(decoder, binary( "7e550104337401903111850622072002454206133574075359513a0000080100000001aa00005ded05e203000000000c06005affb5ffb40a0302dc65100100137e"), Position.KEY_CHARGE, true); diff --git a/src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java index 00b7de094..eb6480213 100644 --- a/src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java @@ -10,6 +10,9 @@ public class IntellitracProtocolDecoderTest extends ProtocolTest { IntellitracProtocolDecoder decoder = new IntellitracProtocolDecoder(null); + verifyPosition(decoder, text( + "359316075744331,20201008181424,12.014662,57.826301,0,76,24,10,997,3,0,0.000,4.208,20201008181424,0")); + verifyNull(decoder, text( "$OK:TRACKING")); diff --git a/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java index 4f5a63188..69abfd837 100644 --- a/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java @@ -14,6 +14,10 @@ public class ItsProtocolDecoderTest extends ProtocolTest { verifyNull(decoder, text( "$LGN,,869867037009679,3.2AIH,9.99546000,N,76.35886167,E")); + verifyAttribute(decoder, text( + "$,C,CTPL,4.0.0,NR,01,L,869247045166383,NA00000000,1,12032020,144453,30.452524,N,077.610351,E,1.4,34.8,14,384.19,1.8,0.8,IDEA P,1,1,14.2,4.17,0,C,22,404,82,0FB1,3B26,516B,0FB1,18,3B25,0FB1,15,5169,0FB1,14,3B27,0FB1,13,0000,00,8083,194.9,0B,*,IP=106.67.5.173"), + Position.KEY_ODOMETER, 194900.0); + verifyPosition(decoder, text( "$,EPB,SEM,868997031721531,NM,14072020112020,A,28.359959,N,076.927566,E,260.93,0.1,0.0,G,NA00000000,N.A0000000,*")); diff --git a/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java index 9f9da26ca..ea7ea19a2 100644 --- a/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java @@ -2,6 +2,7 @@ package org.traccar.protocol; import org.junit.Test; import org.traccar.ProtocolTest; +import org.traccar.model.Position; public class MeitrackProtocolDecoderTest extends ProtocolTest { @@ -10,6 +11,13 @@ public class MeitrackProtocolDecoderTest extends ProtocolTest { MeitrackProtocolDecoder decoder = new MeitrackProtocolDecoder(null); + verifyPositions(decoder, binary( + "2424413132332c3836313538353034333230303836322c4343452c010000000100590015000305010609071b0b081c000939010a07000b1700199e011a9505921a0099c4089c5500c93e00405a000602a8b114000343f12e0604d18806270c654a2e000da20537009bb8963904010e0c0d020300aa7a0af69e0100002a35340d0a")); + + verifyAttribute(decoder, buffer( + "$$F153,867144025101013,AAA,35,25.219431,55.279918,200916155923,V,0,25,0,0,0.0,0,249701532,98374503,424|2|101C|A3AE,0800,0000|0000|0000|02D3|0103,00000011,*A0"), + Position.KEY_INPUT, 8); + verifyPosition(decoder, buffer( "$$O160,863835028611502,AAA,35,7.887840,98.375193,200202020238,A,12,4,0,279,0.6,45,32121,442492,520|3|12DF|015273E2,0000,0000|0000|0000|018D|04F0,00000001,,1,0000*F3")); diff --git a/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java index 605a02b92..22f0974ec 100644 --- a/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java @@ -10,6 +10,9 @@ public class MictrackProtocolDecoderTest extends ProtocolTest { MictrackProtocolDecoder decoder = new MictrackProtocolDecoder(null); + verifyNull(decoder, text( + "mode=Success!")); + verifyPosition(decoder, text( "MT;6;866425031361423;R0;10+190109091803+22.63827+114.02922+2.14+69+2+3744+113"), position("2019-01-09 09:18:03.000", true, 22.63827, 114.02922)); diff --git a/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java index 5898b74a8..00c33ec82 100644 --- a/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java @@ -13,16 +13,22 @@ public class Minifinder2ProtocolDecoderTest extends ProtocolTest { verifyNull(decoder, binary( "ab10150076f1320003100133353534363530373130323933303602105a")); + verifyPositions(decoder, binary( + "ab10cf0382171a0c0110013836353036383034303030303132380924d2a85b5f010296411620d8d9cf13283b5fc601000000e4065500ac4001000b0924dca85b5f0102964116205fd4cf13c53d5fc6010099009f074300ac4001000b0924e6a85b5f010296411620dbd0cf13b83b5fc60d00df00d0074300ac4001000b0924f0a85b5f010296411620a0cccf1339195fc61c000a01fd074000ac4001000b0924faa85b5f010296411620efd9cf135f0b5fc61f00a600b0074200124101000b092404a95b5f010296411620c1eecf13de235fc627005400d3074300124101000b09240ea95b5f010296411620b6f0cf139d505fc628005400280846007d4101000b092418a95b5f01029641162041f2cf13d5835fc62b0056006a084600514201000b092422a95b5f0102964116204ee6cf137ea25fc62100a00091084000514201000b09242ca95b5f01029641162042d6cf130a9a5fc62100fd0085074300b74201000b092436a95b5f010296411620a6d4cf132b6a5fc628000a01250841001e4301000b092440a95b5f01029641162039d1cf1336445fc61200100127084000894301000b09244aa95b5f010296411620bad5cf13eb3d5fc601004e001d084c00894301000b092454a95b5f01029641162042d6cf13163e5fc6000004006e084500894301000b09245ea95b5f01029641162008d9cf1322395fc60100000032084000894301000b092468a95b5f010296411620bfd5cf1345395fc60000080069084c00894301000b092472a95b5f01029641162001d7cf13e8375fc6000000003e085200894301000b09247ca95b5f0102964116204fd7cf136c3a5fc60000000055085400894301000b092486a95b5f010296411620d2d7cf1364395fc600000000a8076c00894301000b092490a95b5f010296411620fdd6cf13383a5fc6000000003c084d00894301000b09249aa95b5f01029641162049d7cf13d9395fc600000000be076c00894301000b0924a4a95b5f010296411620e8d5cf133f375fc6000000001a085900894301000b0924aea95b5f01029641162080d9cf13e3395fc60000000080076c00894301000b0924b8a95b5f010296411620a1d9cf13ac395fc6000000006f075b00894301000b0924c2a95b5f010296411620f3d9cf13b53a5fc60000000090074f00894301000b0924cca95b5f010296411620a1d7cf13aa385fc6000000001d084d00894301000b0924d6a95b5f0102964116208fd8cf1393385fc600000000fc074d00894301000b0924e0a95b5f0102964116206dd7cf13ae395fc600000000ef074d00894301000b0924eaa95b5f01029641162044d8cf1388395fc600000000a5075200894301000b")); + + verifyNotNull(decoder, binary( + "ab10ba034461360201100138363339323130333135313139363009245b364f5f3200ae646a22a76ce873a3ee50b8547595374807b2fad027610438b504d3b5b7819cb2943bb04351a3b094bf8087b878b8502b7306cb11aec85ba0a15044a7dcfe18bdcdf8ac1226a6964e1ab00c8447c13e5dbf480eecf0e436a1c486e987b794a5b8f88340d7b5ad9ca615c91cda092464364f5f3200ae646a22a76ce873a3ee50b8547595374807b2fad027610438b504d3b5b7819cb2943bb04351a3b094bf8087b878b8502b7306cb11aec85ba0a15044a7dcfe18bdcdf8ac1226a6964e1ab00c8447c13e5dbf480eecf0e436a1c486e987b794a5b8f88340d7b5ad9ca615c91cda09246e364f5f3200ae646a22a76ce873a3ee50b8547595374807b2fad027610438b504d3b5b7819cb2943bb04351a3b094bf8087b878b8502b7306cb11aec85ba0a15044a7dcfe18bdcdf8ac1226a6964e1ab00c8447c13e5dbf480eecf0e436a1c486e987b794a5b8f88340d7b5ad9ca615c91cda09246f364f5f3200ae646a22a76ce873a3ee50b8547595374807b2fad027610438b504d3b5b7819cb2943bb04351a3b094bf8087b878b8502b7306cb11aec85ba0a15044a7dcfe18bdcdf8ac1226a6964e1ab00c8447c13e5dbf480eecf0e436a1c486e987b794a5b8f88340d7b5ad9ca615c91cda092478364f5f3200ae646a22a76ce873a3ee50b8547595374807b2fad027610438b504d3b5b7819cb2943bb04351a3b094bf8087b878b8502b7306cb11aec85ba0a15044a7dcfe18bdcdf8ac1226a6964e1ab00c8447c13e5dbf480eecf0e436a1c486e987b794a5b8f88340d7b5ad9ca615c91cda092482364f5f3200ae646a22a76ce873a3ee50b8547595374807b2fad027610438b504d3b5b7819cb2943bb04351a3b094bf8087b878b8502b7306cb11aec85ba0a15044a7dcfe18bdcdf8ac1226a6964e1ab00c8447c13e5dbf480eecf0e436a1c486e987b794a5b8f88340d7b5ad9ca615c91cda09248c364f5f3200ae646a22a76ce873a3ee50b8547595374807b2fad027610438b504d3b5b7819cb2943bb04351a3b094bf8087b878b8502b7306cb11aec85ba0a15044a7dcfe18bdcdf8ac1226a6964e1ab00c8447c13e5dbf480eecf0e436a1c486e987b794a5b8f88340d7b5ad9ca615c91cda092496364f5f3200ae646a22a76ce873a3ee50b8547595374807b2fad027610438b504d3b5b7819cb2943bb04351a3b094bf8087b878b8502b7306cb11aec85ba0a15044a7dcfe18bdcdf8ac1226a6964e1ab00c8447c13e5dbf480eecf0e436a1c486e987b794a5b8f88340d7b5ad9ca615c91cda")); + verifyNotNull(decoder, binary( "ab1024009b3f9742011001383635323039303336333430303235113154cfc95d0a00000080d0c95d0a000000")); - verifyPosition(decoder, binary( + verifyPositions(decoder, binary( "ab103f007e2533000110013335353436353037313032393330360930e09d245d210100000924b49e245d01025b201620e6c03b1ef367420400000000aa026d00c90e0000100110")); - verifyAttributes(decoder, binary( + verifyNotNull(decoder, binary( "ab1845005d39370301100133353836383830303030303338303209245b92b55c84004b610502001000002221ca00050b4a005cc30f4a0056c80f4a003ba90e4a0055c8074a005dc3034a0057c8")); - verifyAttributes(decoder, binary( + verifyNotNull(decoder, binary( "ab185c001db78b03011001333538363838303030303033383032092448bd8a5c82003b130502010000003922ca923bad10f794bd30b5c2cb0595b2944a0c49a4f9b6a4b1e9991e79ba0026bb78c08fb4581faae7ee3fb0e091f5778e96b074a78ed46528")); verifyNotNull(decoder, binary( diff --git a/src/test/java/org/traccar/protocol/MoovboxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MoovboxProtocolDecoderTest.java new file mode 100644 index 000000000..9b3f6823a --- /dev/null +++ b/src/test/java/org/traccar/protocol/MoovboxProtocolDecoderTest.java @@ -0,0 +1,19 @@ +package org.traccar.protocol; + +import io.netty.handler.codec.http.HttpMethod; +import org.junit.Test; +import org.traccar.ProtocolTest; + +public class MoovboxProtocolDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + MoovboxProtocolDecoder decoder = new MoovboxProtocolDecoder(null); + + verifyPositions(decoder, request(HttpMethod.POST, "/", + buffer("<gps id=\"911\">\n<coordinates><coordinate>\n<fix>3</fix>\n<time>1597580050</time>\n<latitude>100.726257</latitude>\n<longitude>13.821351</longitude>\n<altitude>9.500000</altitude>\n<climb>0.000000</climb>\n<speed>0.064000</speed>\n<separation>-27.300000</separation>\n<track>0.000000</track>\n<satellites>9</satellites>\n</coordinate></coordinates>\n</gps>"))); + + } + +} diff --git a/src/test/java/org/traccar/protocol/NetProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NetProtocolDecoderTest.java new file mode 100644 index 000000000..678e5c6c3 --- /dev/null +++ b/src/test/java/org/traccar/protocol/NetProtocolDecoderTest.java @@ -0,0 +1,21 @@ +package org.traccar.protocol; + +import org.junit.Test; +import org.traccar.ProtocolTest; + +public class NetProtocolDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + NetProtocolDecoder decoder = new NetProtocolDecoder(null); + + verifyPosition(decoder, text( + "@L03686090604017761712271020161807037078881037233751000000010F850036980A4000")); + + verifyPosition(decoder, text( + "@L0368609060401776171223102005072803703296103721462100008009000000300B12B000")); + + } + +} diff --git a/src/test/java/org/traccar/protocol/PolteProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PolteProtocolDecoderTest.java new file mode 100644 index 000000000..61ee6771f --- /dev/null +++ b/src/test/java/org/traccar/protocol/PolteProtocolDecoderTest.java @@ -0,0 +1,19 @@ +package org.traccar.protocol; + +import io.netty.handler.codec.http.HttpMethod; +import org.junit.Test; +import org.traccar.ProtocolTest; + +public class PolteProtocolDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + PolteProtocolDecoder decoder = new PolteProtocolDecoder(null); + + verifyPosition(decoder, request(HttpMethod.POST, "/", + buffer("{\"_id\":\"5f75cf7b02c5023bfc0beaf7\",\"location\":{\"LocationMetrics\":{\"EnvironmentDensity\":1,\"LocationType\":2,\"carrierInfo\":{\"aux\":{\"PLMN\":\"310410\",\"country\":\"United States\",\"name\":\"ATT Wireless Inc\"},\"crs\":{\"PLMN\":\"310410\",\"country\":\"United States\",\"name\":\"ATT Wireless Inc\"}},\"hdop\":1850000,\"leversion\":\"2.2.18-20200729T140651\",\"towercount\":1},\"altitude\":0.0011297669261693954,\"confidence\":783.7972188868215,\"detected_at\":1601556342,\"latitude\":29.77368956725161,\"longitude\":-98.26530342694024,\"towerDB\":\"default\",\"ueToken\":\"ALT12503DE04336CB2E3A4A113FCDE05DF05A6F\",\"uid\":\"WZuDMv5Je\"},\"report\":{\"battery\":{\"count\":555,\"level\":100,\"voltage\":3.52},\"event\":3,\"time\":\"2020-10-01T12:45:48.207Z\"},\"time\":\"2020-10-01T12:45:42Z\",\"ueToken\":\"ALT12503DE04336CB2E3A4A113FCDE05DF05A6F\",\"uid\":\"WZuDMv5Je\"}"))); + + } + +} diff --git a/src/test/java/org/traccar/protocol/PstFrameEncoderTest.java b/src/test/java/org/traccar/protocol/PstFrameEncoderTest.java new file mode 100644 index 000000000..bc458c398 --- /dev/null +++ b/src/test/java/org/traccar/protocol/PstFrameEncoderTest.java @@ -0,0 +1,20 @@ +package org.traccar.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import org.junit.Test; +import org.traccar.ProtocolTest; + +public class PstFrameEncoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + PstFrameEncoder encoder = new PstFrameEncoder(); + + ByteBuf result = Unpooled.buffer(); + encoder.encode(null, binary("2FAF0B10059A0000B001022FAF0B10E91349A2AD3B1DAD2FF8A78228A58F"), result); + verifyFrame(binary("282FAF0B10059A0000B001022FAF0B10E91349A2AD3B1DAD2FF8A7822768A58F29"), result); + } + +} diff --git a/src/test/java/org/traccar/protocol/PstProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/PstProtocolEncoderTest.java new file mode 100644 index 000000000..ddf6d460c --- /dev/null +++ b/src/test/java/org/traccar/protocol/PstProtocolEncoderTest.java @@ -0,0 +1,35 @@ +package org.traccar.protocol; + +import org.junit.Test; +import org.traccar.ProtocolTest; +import org.traccar.model.Command; + +public class PstProtocolEncoderTest extends ProtocolTest { + + @Test + public void testEncodeEngineStop() { + + PstProtocolEncoder encoder = new PstProtocolEncoder(null); + + Command command = new Command(); + command.setDeviceId(1); + command.setType(Command.TYPE_ENGINE_STOP); + + verifyCommand(encoder, command, binary("860ddf790600000001060002ffffffffe42b")); + + } + + @Test + public void testEncodeEngineResume() { + + PstProtocolEncoder encoder = new PstProtocolEncoder(null); + + Command command = new Command(); + command.setDeviceId(1); + command.setType(Command.TYPE_ENGINE_RESUME); + + verifyCommand(encoder, command, binary("860ddf790600000001060001ffffffff0af9")); + + } + +} diff --git a/src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java index cb1c1eb0e..a7c8be21f 100644 --- a/src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java @@ -15,6 +15,9 @@ public class Pt502ProtocolDecoderTest extends ProtocolTest { "24504844302c3936302cffd8ffdb008400140e0f120f0d14121012171514181e32211e1c1c1e3d2c2e243249404c4b47404645505a736250556d5645466488656d777b8182814e608d978c7d96737e817c011517171e1a1e3b21213b7c5346537c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7cffc000110800f0014003012100021101031101ffdd0004000affc401a20000010501010101010100000000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa0100030101010101010101010000000000000102030405060708090a0b1100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00e5292800ef450020a2800a2801d49400b450014b40052e2800a69340094a05007fffd0e5d14b10055b51b00c76a00527273494005250014500251400525001450015347c25003a928010d25007ffd1e52909a00290d0014b40052d0014500145002e297b50018a280109a6d002d2e2803fffd2e7a04da3777a94fbd0025140052500145002514005250014940054e381400b494008690d007fffd3e4f345001486800a5a005a2800a2801680280168a002909e280100cd028016a48937bfb5007fffd4c5038a42280128a004a280128a003ad2500251400945002a8cb0a9a80133450026692803ffd5e4e8a004a2801694500145002d18a005c5140052e280109a69a0029680140abb147b139eb401ffd6c62290d00251400949400114940052500252d002525003e31c93525002521a004a4a00ffd7e4a8a00281400a29d40094b40053ba500252d0018a31400d3cd250018cd2d005ab58777ccdd074ab645007ffd0c72290d00348a2801280")); verifyPosition(decoder, buffer( + "$POS,718005258,121930.000,V,0514.0960,S,14547.3245,E,0.0,0.0,151020,,/00000,00000/0,0,0,0/106945600//f00//")); + + verifyPosition(decoder, buffer( "$PHO3821-1,1156802639,022125.000,A,0707.0014,N,07307.3725,W,0.0,0.1,110418,,,A/00000,00000/0,0,0,0/500//fd4//")); verifyPosition(decoder, buffer( diff --git a/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java index 53f03730a..f31773e0c 100644 --- a/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java @@ -12,7 +12,8 @@ public class SigfoxProtocolDecoderTest extends ProtocolTest { SigfoxProtocolDecoder decoder = new SigfoxProtocolDecoder(null); - verifyPosition(decoder, request(HttpMethod.POST, "/", buffer("{ \"device\":\"BFE47E\", \"time\":1590497040, \"data\":\"10297eb01e621122070000be\", \"seqNumber\":8, \"deviceTypeId\":\"5ecb8bfac563d620cc9e6798\", \"ack\":false }"))); + verifyPosition(decoder, request(HttpMethod.POST, "/", + buffer("{ \"device\":\"BFE47E\", \"time\":1590497040, \"data\":\"10297eb01e621122070000be\", \"seqNumber\":8, \"deviceTypeId\":\"5ecb8bfac563d620cc9e6798\", \"ack\":false }"))); verifyAttributes(decoder, request(HttpMethod.POST, "/", buffer("{\"messageType\":\"accelerometer\",\"deviceId\":\"testdev001\",\"snr\":\"1234\",\"rssi\":\"-120.00\",\"station\":\"5678\",\"seqNum\":\"9123\",\"newPosition\":false,\"latitude\":\"null\",\"longitude\":\"null\",\"positionTime\":\"null\",\"moving\":true,\"magChange\":\"true\",\"magStatus\":\"true\",\"temperature\":\"7.5\",\"battery\":\"null\",\"batteryPercentage\":\"null\",\"lastSeen\":\"1582560425\",\"fwVersion\":\"null\",\"dlConfig\":\"null\",\"recievedPayload\":\"cb020051\"}"))); diff --git a/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java index 459dad978..1dd96d8ca 100644 --- a/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java @@ -11,6 +11,18 @@ public class StarLinkProtocolDecoderTest extends ProtocolTest { StarLinkProtocolDecoder decoder = new StarLinkProtocolDecoder(null); + decoder.setFormat("#IMEI#,#EDT#,#PDT#,#LAT#,#LONG#,#SPD#,#IGN#,#ODO#,#DUR#,#TDUR#,#LAC#,#CID#,#VIN#,#VBAT#,#EID#,#EDSC#,#DRV#,#SATU#,#CSS#,#OUT1#,#OUT2#,#IN2#,#IND#"); + + verifyAttribute(decoder, text( + "$SLU351580050543640,06,101,351580050543640,200927184734,200927184724,+4222.4186,+00153.1426,000.0,0,000008,,582,21269,214241628,00.213,03.407,09,Lost Power,0,5,96,0,0,0,1,,,,,,,,,,,,*10"), + Position.KEY_RSSI, 96); + + decoder.setFormat("#EDT#,#EID#,#PDT#,#LAT#,#LONG#,#SPD#,#HEAD#,#ODO#,#LAC#,#CID#,#VIN#,#VBAT#,#TI1#,#TS1#,#TV1#,#TH1#,#TD1#,#EDSC#,#TI2#,#TS2#,#TV2#,#TH2#,#TD2#"); + + verifyAttribute(decoder, text( + "$SLU351580050356894,06,1192,200811104100,01,200811104058,+4121.8168,+00205.1158,000.0,069,000069,2080,15139043,12.221,03.533,D469062D44D6,1,29.7,56.9,CAEaBtRpBi1E1jhLSJoYKLkEINIEWABgAmgAcAJ4AIABAIgBAA==,Location,D2567108639E,1,30.2,55.3,CAIaBtJWcQhjnjhPSPIXKKkEINwEWEpgA2gAcAJ4AIABAIgBAA==,1,99*5A"), + "sensor2Voltage", 3058 * 0.001); + decoder.setFormat("#IMEI#,#EDT#,#PDT#,#LAT#,#LONG#,#SPD#,#IGN#,#ODO#,#DUR#,#TDUR#,#LAC#,#CID#,#VIN#,#VBAT#,#EID#,#EDSC#,#DRV#,#SATU#,#CSS#,#OUT1#,#OUT2#"); verifyAttribute(decoder, text( diff --git a/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java index 549402697..847f6707d 100644 --- a/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java @@ -63,12 +63,28 @@ public class SuntechProtocolDecoderTest extends ProtocolTest { } @Test + public void testDecodeDriver() throws Exception { + + SuntechProtocolDecoder decoder = new SuntechProtocolDecoder(null); + + verifyAttribute(decoder, buffer( + "ST300HTE;511050566;45;308;20200909;13:38:38;0;12.50;001354;0.0;1;0;1;1;0;-27.636632;-052.277933;-27.636675;-052.277947;000.000;002.296;0;00000000000000"), + Position.KEY_DRIVER_UNIQUE_ID, "00000000000000"); + + verifyAttribute(decoder, buffer( + "ST300HTE;100850001;04;248;20110101;00:13:52;167559;12.28;004005;0.0;1;0;3;3;0;-22.881018;-047.070831;-22.881018;-047.070831;000.000;000.000;0;0;3;0;0;0;01E04D44160000"), + Position.KEY_DRIVER_UNIQUE_ID, "01E04D44160000"); + + } + + @Test public void testDecode() throws Exception { SuntechProtocolDecoder decoder = new SuntechProtocolDecoder(null); - verifyPosition(decoder, buffer( - "ALT;0470001109;BFFFFF;47;1.0.2;0;20200511;22:39:38;0B29790F;310;410;2C13;30;+34.928093;-83.704295;0.94;30.95;10;1;00000001;00000000;15;;;00018003;4.1;14.13;629;11905")); + verifyAttribute(decoder, buffer( + "STT;0560001616;BFFFFF;56;1.0.15;1;20200219;20:52:25;00008D6C;334;20;0925;24;+20.741764;-103.430364;0.00;0.00;19;1;00000001;00000000;2;1;1765;00008003;0.0;12.14;136598"), + Position.KEY_INDEX, 1765); verifyAttribute(decoder, buffer( "STT;0560001616;BFFFFF;56;1.0.15;1;20200219;20:52:25;00008D6C;334;20;0925;24;+20.741764;-103.430364;0.00;0.00;19;1;00000001;00000000;2;1;1765;00008003;0.0;12.14;136598"), diff --git a/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java index c0511f2a1..25fef58f1 100644 --- a/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java @@ -11,6 +11,9 @@ public class T55ProtocolDecoderTest extends ProtocolTest { T55ProtocolDecoder decoder = new T55ProtocolDecoder(null); + verifyPosition(decoder, text( + "QZE,868994033976700,35,28062020,113553,22.13673,114.57263,0,22,A,0")); + verifyNull(decoder, text( "$DEVID,0x0103846677F21422*41")); diff --git a/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java index 48c535c96..d2532d091 100644 --- a/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java @@ -11,6 +11,10 @@ public class T800xProtocolDecoderTest extends ProtocolTest { T800xProtocolDecoder decoder = new T800xProtocolDecoder(null); + verifyAttribute(decoder, binary( + "2727020049052e086528404072393849002008060310110000000068b7c8c286eaa441000000008000008100001617410700019ce782b0001e000002581e00000530d4801f00000000"), + Position.KEY_BATTERY_LEVEL, 100); + verifyPosition(decoder, binary( "262602005308090865284040309670000f000f0f0000005a47c000050100000020000000008bfd0020022505185300004041dcc9d6c243b3c6410000012712400000000009e2ffffffffffffffffffffffff09")); diff --git a/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java index 5b2df38db..2f43733c4 100644 --- a/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java @@ -3,7 +3,6 @@ package org.traccar.protocol; import org.junit.Ignore; import org.junit.Test; import org.traccar.ProtocolTest; -import org.traccar.model.Position; public class TeltonikaProtocolDecoderTest extends ProtocolTest { @@ -15,6 +14,9 @@ public class TeltonikaProtocolDecoderTest extends ProtocolTest { verifyNull(decoder, binary( "000F313233343536373839303132333435")); + verifyPositions(decoder, binary( + "00000000000000858e0200000174431aadc100061d888f21000e8a0032002e0c000001810001000000000000000000010181001711210102030405060708090a0b0c0d0e0f10020b010ad000000174431a389100061d888f21000e8a0033002e0d000001810001000000000000000000010181001711210102030405060708090a0b0c0d0e0f10020b010ad2020000492b")); + verifyPositions(decoder, false, binary( "000000000000001C0D01050000001056E924222347455420444154414F524445520D0A0100004990")); @@ -71,16 +73,16 @@ public class TeltonikaProtocolDecoderTest extends ProtocolTest { verifyPositions(decoder, binary( "000000000000003508010000014f8e016420002141bbaf0f4e96a7fffa0000120000000602010047030242669c92000002c7000000009100000000000100002df3")); - + verifyPositions(decoder, binary( "00000000000000A7080400000113fc208dff000f14f650209cca80006f00d60400040004030101150316030001460000015d0000000113fc17610b000f14ffe0209cc580006e00c00500010004030101150316010001460000015e0000000113fc284945000f150f00209cd200009501080400000004030101150016030001460000015d0000000113fc267c5b000f150a50209cccc0009300680400000004030101150016030001460000015b00040000")); - + verifyPositions(decoder, binary( "000000000000014708060000013e5a60a4cb003fa7b780fc424518004200000a000000090501010200b300b400f000034268a746011818000001c700000000000000013e5dc8ba28003fa7c080fc4246040001000005000000090501010200b300b400f001034268b44600ef18000001c700000000000000013e5dc90455003fa7b640fc424388003a0000070000f0090501010200b300b400f000034268dc4600f718000001c70000001d000000013e5dc9d368003fa7b800fc4244300049000004000000090501010200b300b400f001034267de46010718000001c700000000000000013e5dca311d003fa7b680fc4243cc00420000070000f0090501010200b300b400f0000342685346010b18000001c700000000000000013e5dcfafe9003fa7b600fc4242f0003d000008000000090501010200b300b400f0000342685246011918000001c700000000000600000275")); verifyPositions(decoder, binary( "000000000000002c08010000013eff8d6f9800173295002111f400008100ae0b0000000401010003090016432980422f7200000100007a5d")); - + verifyPositions(decoder, binary( "00000000000000c7070441bf9db00fff425adbd741ca6e1e009e1205070001030b160000601a02015e02000314006615000a160067010500000ce441bf9d920fff425adbb141ca6fc900a2b218070001030b160000601a02015e02000314006615000a160067010500000cc641bf9d740fff425adbee41ca739200b6c91e070001030b1f0000601a02015f02000314006615000a160066010500000ca841bf9cfc0fff425adba041ca70c100b93813070001030b1f0000601a02015f02000314002315000a160025010500000c3004000000")); diff --git a/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java b/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java index 01d63bfa3..31e748f27 100644 --- a/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java @@ -3,8 +3,6 @@ package org.traccar.protocol; import org.junit.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; - public class UlbotechFrameDecoderTest extends ProtocolTest { @Test diff --git a/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java index 8c820183b..a2bc1b46e 100644 --- a/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java @@ -10,6 +10,9 @@ public class UlbotechProtocolDecoderTest extends ProtocolTest { UlbotechProtocolDecoder decoder = new UlbotechProtocolDecoder(null); + verifyPosition(decoder, binary( + "f801010353323083177450a703f6f0010efe55a31a0923d01400050070007003040a42000004040070cca00506039b1876220f060800000000000000000725310553410c0c9e310d05310f4641100440311119411f00476101810f8000310487411f00480804203a14c009033320159310f8")); + verifyNull(decoder, buffer( "*TS01,353323081464660#")); diff --git a/src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java b/src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java index 8fc628bdb..b91cd0ebf 100644 --- a/src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java @@ -3,8 +3,6 @@ package org.traccar.protocol; import org.junit.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; - public class XexunFrameDecoderTest extends ProtocolTest { @Test @@ -12,11 +10,11 @@ public class XexunFrameDecoderTest extends ProtocolTest { XexunFrameDecoder decoder = new XexunFrameDecoder(); - assertEquals( + verifyFrame( binary("4750524d432c3230353933352e3030302c412c353134302e343335302c4e2c3530312e303638362c452c302e30302c302e30302c3132313031352c30302c303030302e302c412a37302c462c2c696d65693a3335393538373031343731383339322c"), decoder.decode(null, null, binary("313531303132313435392c2b33313635323435343932372c4750524d432c3230353933352e3030302c412c353134302e343335302c4e2c3530312e303638362c452c302e30302c302e30302c3132313031352c30302c303030302e302c412a37302c462c2c696d65693a3335393538373031343731383339322c31323249"))); - assertEquals( + verifyFrame( binary("4750524d432c3130333733312e3633362c412c343534352e353236362c4e2c30303434382e383235392c452c32312e31322c3237362e30312c3135303631352c2c2c412a35372c4c2c2c20696d65693a3031333934393030323032363637352c"), decoder.decode(null, null, binary("3135303631353132333733312c2b33333634373338343631312c4750524d432c3130333733312e3633362c412c343534352e353236362c4e2c30303434382e383235392c452c32312e31322c3237362e30312c3135303631352c2c2c412a35372c4c2c2c20696d65693a3031333934393030323032363637352c30342c333532322e392c463a332e3732562c302c3134322c32313734342c3230382c30312c303730322c394338430a0d"))); diff --git a/src/test/java/org/traccar/speedlimit/OverpassSpeedLimitProviderTest.java b/src/test/java/org/traccar/speedlimit/OverpassSpeedLimitProviderTest.java new file mode 100644 index 000000000..dcac78f80 --- /dev/null +++ b/src/test/java/org/traccar/speedlimit/OverpassSpeedLimitProviderTest.java @@ -0,0 +1,35 @@ +package org.traccar.speedlimit; + +import org.junit.Ignore; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class OverpassSpeedLimitProviderTest { + + @Ignore + @Test + public void test() throws Exception { + testLocationProvider(); + } + + public void testLocationProvider() throws Exception { + SpeedLimitProvider provider = new OverpassSpeedLimitProvider("http://8.8.8.8/api/interpreter"); + + provider.getSpeedLimit(34.74767, -82.48098, new SpeedLimitProvider.SpeedLimitProviderCallback() { + @Override + public void onSuccess(double speedLimit) { + assertEquals(52.1, speedLimit, 0.1); + } + + @Override + public void onFailure(Throwable e) { + fail(); + } + }); + + Thread.sleep(Long.MAX_VALUE); + } + +} diff --git a/src/test/java/org/traccar/web/WebServerTest.java b/src/test/java/org/traccar/web/WebServerTest.java index 5a79fbac2..ba4124e44 100644 --- a/src/test/java/org/traccar/web/WebServerTest.java +++ b/src/test/java/org/traccar/web/WebServerTest.java @@ -6,8 +6,6 @@ import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.DataSource; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class WebServerTest { @@ -15,12 +13,7 @@ public class WebServerTest { @Test public void contextTest() throws NamingException { DataSource mockDataSource = (DataSource) Proxy.newProxyInstance(getClass().getClassLoader(), - new Class[] { DataSource.class }, new InvocationHandler() { - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - return null; - } - }); + new Class[]{DataSource.class}, (proxy, method, args) -> null); Context context = new InitialContext(); context.bind("java:/DefaultDS", mockDataSource); diff --git a/swagger.json b/swagger.json index 334ed486a..e3df09515 100644 --- a/swagger.json +++ b/swagger.json @@ -1,2281 +1,3485 @@ { - "swagger": "2.0", - "info": { - "version": "4.10", - "title": "traccar" + "openapi": "3.0.1", + "info": { + "title": "Traccar", + "version": "4.10", + "description": "Traccar GPS tracking server API documentation. To use the API you need to have a server instance. For testing purposes you can use one of free [demo servers](https://www.traccar.org/demo-server/). For production use you can install your own server or get a [subscription service](https://www.traccar.org/product/tracking-server/).", + "contact": { + "name": "Traccar Support", + "url": "https://www.traccar.org/", + "email": "support@traccar.org" }, - "host": "demo.traccar.org", - "basePath": "/api", - "schemes": [ - "http" - ], - "security": [ - { - "basicAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "paths": { - "/commands": { - "get": { - "summary": "Fetch a list of Saved Commands", - "description": "Without params, it returns a list of Drivers the user has access to", - "parameters": [ - { - "$ref": "#/parameters/all" - }, - { - "$ref": "#/parameters/userId" - }, - { - "$ref": "#/parameters/deviceId" - }, - { - "$ref": "#/parameters/groupId" - }, - { - "$ref": "#/parameters/refresh" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/Command" - } - } - } - } - }, - "post": { - "summary": "Create a Saved Command", - "parameters": [ - { - "$ref": "#/parameters/Command" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Command" - } - } - } - } - }, - "/commands/{id}": { - "put": { - "summary": "Update a Saved Command", - "parameters": [ - { - "$ref": "#/parameters/entityId" - }, - { - "$ref": "#/parameters/Command" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Command" - } - } - } - }, - "delete": { - "summary": "Delete a Saved Command", - "parameters": [ - { - "$ref": "#/parameters/entityId" - } - ], - "responses": { - "204": { - "description": "No Content" - } - } - } - }, - "/commands/send": { - "get": { - "summary": "Fetch a list of Saved Commands supported by Device at the moment", - "description": "Return a list of saved commands linked to Device and its groups, filtered by current Device protocol support", - "parameters": [ - { - "$ref": "#/parameters/deviceId" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/Command" - } - } - }, - "400": { - "description": "Could happen when the user doesn't have permission for the device" - } - } - }, - "post": { - "summary": "Dispatch commands to device", - "description": "Dispatch a new command or Saved Command if _body.id_ set", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/Command" - } - } - ], - "responses": { - "200": { - "description": "Command sent", - "schema": { - "$ref": "#/definitions/Command" - } - }, - "202": { - "description": "Command queued", - "schema": { - "$ref": "#/definitions/Command" - } - }, - "400": { - "description": "Could happen when the user doesn't have permission or an incorrect command _type_ for the device" - } - } - } - }, - "/commands/types": { - "get": { - "summary": "Fetch a list of available Commands for the Device or all possible Commands if Device ommited", - "parameters": [ - { - "name": "deviceId", - "in": "query", - "description" : "Internal device identifier. Only works if device has already reported some locations", - "required" : false, - "type": "integer" - }, - { - "name": "protocol", - "in": "query", - "description" : "Protocol name. Can be used instead of device id", - "required" : false, - "type": "string" - }, - { - "name": "textChannel", - "in": "query", - "description" : "When `true` return SMS commands. If not specified or `false` return data commands", - "required" : false, - "type": "boolean" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/CommandType" - } - } - }, - "400": { - "description": "Could happen when trying to fetch from a device the user does not have permission" - } - } - } - }, - "/devices": { - "get": { - "summary": "Fetch a list of Devices", - "description": "Without any params, returns a list of the user's devices", - "parameters": [ - { - "$ref": "#/parameters/all" - }, - { - "$ref": "#/parameters/userId" - }, - { - "name" : "id", - "in" : "query", - "description" : "To fetch one or more devices. Multiple params can be passed like `id=31&id=42`", - "required" : false, - "type" : "integer", - "collectionFormat" : "multi" - }, - { - "name" : "uniqueId", - "in" : "query", - "description" : "To fetch one or more devices. Multiple params can be passed like `uniqueId=333331&uniqieId=44442`", - "required" : false, - "type" : "string", - "collectionFormat" : "multi" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/Device" - } - } - }, - "400": { - "description": "No permission" - } - } - }, - "post": { - "summary": "Create a Device", - "parameters": [ - { - "$ref": "#/parameters/Device" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Device" - } - } - } + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "servers": [ + { + "url": "https://demo.traccar.org/api", + "description": "Demo Server 1" + }, + { + "url": "https://demo2.traccar.org/api", + "description": "Demo Server 2" + }, + { + "url": "https://demo3.traccar.org/api", + "description": "Demo Server 3" + }, + { + "url": "https://server.traccar.org/api", + "description": "Subscription Server" + }, + { + "url": "http://{host}:{port}/api", + "description": "Other Server", + "variables": { + "host": { + "default": "localhost" + }, + "port": { + "enum": [ + "8082", + "80" + ], + "default": "8082" + } + } + } + ], + "security": [ + { + "basicAuth": [] + } + ], + "tags": [ + { + "name": "Server", + "description": "Server information" + }, + { + "name": "Session", + "description": "User session management" + }, + { + "name": "Devices", + "description": "Device management" + }, + { + "name": "Groups", + "description": "Group management" + }, + { + "name": "Users", + "description": "User management" + }, + { + "name": "Permissions", + "description": "User permissions and other object linking" + }, + { + "name": "Positions", + "description": "Retrieving raw location information" + }, + { + "name": "Events", + "description": "Retrieving event information" + }, + { + "name": "Reports", + "description": "Reports generation" + }, + { + "name": "Notifications", + "description": "User notifications management" + }, + { + "name": "Geofences", + "description": "Geofence management" + }, + { + "name": "Commands", + "description": "Sending commands to devices and stored command management" + }, + { + "name": "Attributes", + "description": "Computed attributes management" + }, + { + "name": "Drivers", + "description": "Drivers management" + }, + { + "name": "Maintenance", + "description": "Maintenance management" + }, + { + "name": "Calendars", + "description": "Calendar management" + }, + { + "name": "Statistics", + "description": "Retrieving server statistics" + } + ], + "paths": { + "/commands": { + "get": { + "summary": "Fetch a list of Saved Commands", + "tags": [ + "Commands" + ], + "description": "Without params, it returns a list of Drivers the user has access to", + "parameters": [ + { + "name": "all", + "in": "query", + "description": "Can only be used by admins or managers to fetch all entities", + "schema": { + "type": "boolean" } - }, - "/devices/{id}": { - "put": { - "summary": "Update a Device", - "parameters": [ - { - "$ref": "#/parameters/entityId" - }, - { - "$ref": "#/parameters/Device" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Device" - } - } - } - }, - "delete": { - "summary": "Delete a Device", - "parameters": [ - { - "$ref": "#/parameters/entityId" - } - ], - "responses": { - "204": { - "description": "No Content" - } - } + }, + { + "name": "userId", + "in": "query", + "description": "Standard users can use this only with their own _userId_", + "schema": { + "type": "integer" } - }, - "/devices/{id}/accumulators": { - "put": { - "summary": "Update total distance and hours of the Device", - "parameters": [ - { - "$ref": "#/parameters/entityId" - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/DeviceAccumulators" - } - } - ], - "responses": { - "204": { - "description": "No Content" - } - } + }, + { + "name": "deviceId", + "in": "query", + "description": "Standard users can use this only with _deviceId_s, they have access to", + "schema": { + "type": "integer" } - }, - "/groups": { - "get": { - "summary": "Fetch a list of Groups", - "description": "Without any params, returns a list of the Groups the user belongs to", - "parameters": [ - { - "$ref": "#/parameters/all" - }, - { - "$ref": "#/parameters/userId" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/Group" - } - } - } - } - }, - "post": { - "summary": "Create a Group", - "parameters": [ - { - "$ref": "#/parameters/Group" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Group" - } - }, - "400": { - "description": "No permission" - } - } + }, + { + "name": "groupId", + "in": "query", + "description": "Standard users can use this only with _groupId_s, they have access to", + "schema": { + "type": "integer" } - }, - "/groups/{id}": { - "put": { - "summary": "Update a Group", - "parameters": [ - { - "$ref": "#/parameters/entityId" - }, - { - "$ref": "#/parameters/Group" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Group" - } - } - } - }, - "delete": { - "summary": "Delete a Group", - "parameters": [ - { - "$ref": "#/parameters/entityId" - } - ], - "responses": { - "204": { - "description": "No Content" - } - } + }, + { + "name": "refresh", + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Command" + } + } + } + } + } + } + }, + "post": { + "summary": "Create a Saved Command", + "tags": [ + "Commands" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Command" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Command" + } + } + } + } + }, + "x-codegen-request-body-name": "body" + } + }, + "/commands/{id}": { + "put": { + "summary": "Update a Saved Command", + "tags": [ + "Commands" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Command" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Command" + } + } + } + } + }, + "x-codegen-request-body-name": "body" + }, + "delete": { + "summary": "Delete a Saved Command", + "tags": [ + "Commands" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "204": { + "description": "No Content", + "content": {} + } + } + } + }, + "/commands/send": { + "get": { + "summary": "Fetch a list of Saved Commands supported by Device at the moment", + "description": "Return a list of saved commands linked to Device and its groups, filtered by current Device protocol support", + "tags": [ + "Commands" + ], + "parameters": [ + { + "name": "deviceId", + "in": "query", + "description": "Standard users can use this only with _deviceId_s, they have access to", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Command" + } + } + } + } + }, + "400": { + "description": "Could happen when the user doesn't have permission for the device", + "content": {} + } + } + }, + "post": { + "summary": "Dispatch commands to device", + "description": "Dispatch a new command or Saved Command if _body.id_ set", + "tags": [ + "Commands" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Command" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Command sent", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Command" + } + } + } + }, + "202": { + "description": "Command queued", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Command" + } + } + } + }, + "400": { + "description": "Could happen when the user doesn't have permission or an incorrect command _type_ for the device", + "content": {} + } + }, + "x-codegen-request-body-name": "body" + } + }, + "/commands/types": { + "get": { + "summary": "Fetch a list of available Commands for the Device or all possible Commands if Device ommited", + "tags": [ + "Commands" + ], + "parameters": [ + { + "name": "deviceId", + "in": "query", + "description": "Internal device identifier. Only works if device has already reported some locations", + "schema": { + "type": "integer" } - }, - "/permissions": { - "post": { - "summary": "Link an Object to another Object", - "parameters": [ - { - "$ref": "#/parameters/Permission" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Permission" - } - }, - "400": { - "description": "No permission" - } - } - }, - "delete": { - "summary": "Unlink an Object from another Object", - "parameters": [ - { - "$ref": "#/parameters/Permission" - } - ], - "responses": { - "204": { - "description": "No Content" - } - } + }, + { + "name": "protocol", + "in": "query", + "description": "Protocol name. Can be used instead of device id", + "schema": { + "type": "string" } - }, - "/positions": { - "get": { - "summary" : "Fetches a list of Positions", - "description" : "Without any params, it returns a list of last known positions for all the user's Devices. _from_ and _to_ fields are not required with _id_", - "consumes": [ - "application/json", - "text/csv", - "application/gpx+xml" - ], - "produces": [ - "application/json", - "text/csv", - "application/gpx+xml" - ], - "parameters": [ - { - "name": "deviceId", - "in": "query", - "description": "_deviceId_ is optional, but requires the _from_ and _to_ parameters when used", - "required": false, - "type": "integer" - }, - { - "name": "from", - "in": "query", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "required": false, - "type": "string", - "format": "date-time" - }, - { - "name": "to", - "in": "query", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "required": false, - "type": "string", - "format": "date-time" - }, - { - "name" : "id", - "in" : "query", - "description" : "To fetch one or more positions. Multiple params can be passed like `id=31&id=42`", - "required" : false, - "type" : "integer", - "collectionFormat" : "multi" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/Position" - } - } - } - } + }, + { + "name": "textChannel", + "in": "query", + "description": "When `true` return SMS commands. If not specified or `false` return data commands", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CommandType" + } + } + } + } + }, + "400": { + "description": "Could happen when trying to fetch from a device the user does not have permission", + "content": {} + } + } + } + }, + "/devices": { + "get": { + "summary": "Fetch a list of Devices", + "description": "Without any params, returns a list of the user's devices", + "tags": [ + "Devices" + ], + "parameters": [ + { + "name": "all", + "in": "query", + "description": "Can only be used by admins or managers to fetch all entities", + "schema": { + "type": "boolean" } - }, - "/server": { - "get": { - "summary": "Fetch Server information", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Server" - } - } - } - }, - "put": { - "summary": "Update Server information", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/Server" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Server" - } - } - } + }, + { + "name": "userId", + "in": "query", + "description": "Standard users can use this only with their own _userId_", + "schema": { + "type": "integer" } - }, - "/session": { - "get": { - "summary": "Fetch Session information", - "consumes": [ - "application/x-www-form-urlencoded" - ], - "parameters": [ - { - "name": "token", - "in": "query", - "required": false, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/User" - } - }, - "404": { - "description": "Not Found" - } - } - }, - "post": { - "summary": "Create a new Session", - "consumes": [ - "application/x-www-form-urlencoded" - ], - "parameters": [ - { - "name": "email", - "in": "formData", - "required": true, - "type": "string" - }, - { - "name": "password", - "in": "formData", - "required": true, - "type": "string", - "format": "password" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/User" - } - }, - "401": { - "description": "Unauthorized" - } - } - }, - "delete": { - "summary": "Close the Session", - "consumes": [ - "application/x-www-form-urlencoded" - ], - "parameters": [], - "responses": { - "204": { - "description": "No Content" - } - } + }, + { + "name": "id", + "in": "query", + "description": "To fetch one or more devices. Multiple params can be passed like `id=31&id=42`", + "schema": { + "type": "integer" } - }, - "/users": { - "get": { - "summary": "Fetch a list of Users", - "parameters": [ - { - "name": "userId", - "in": "query", - "description": "Can only be used by admin or manager users", - "required": false, - "type": "string" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/User" - } - } - }, - "400": { - "description": "No Permission" - } - } - }, - "post": { - "summary": "Create a User", - "parameters": [ - { - "$ref": "#/parameters/User" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/User" - } - } - } + }, + { + "name": "uniqueId", + "in": "query", + "description": "To fetch one or more devices. Multiple params can be passed like `uniqueId=333331&uniqieId=44442`", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Device" + } + } + } + } + }, + "400": { + "description": "No permission", + "content": {} + } + } + }, + "post": { + "summary": "Create a Device", + "tags": [ + "Devices" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Device" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Device" + } + } + } + } + }, + "x-codegen-request-body-name": "body" + } + }, + "/devices/{id}": { + "put": { + "summary": "Update a Device", + "tags": [ + "Devices" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Device" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Device" + } + } + } + } + }, + "x-codegen-request-body-name": "body" + }, + "delete": { + "summary": "Delete a Device", + "tags": [ + "Devices" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "204": { + "description": "No Content", + "content": {} + } + } + } + }, + "/devices/{id}/accumulators": { + "put": { + "summary": "Update total distance and hours of the Device", + "tags": [ + "Devices" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceAccumulators" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "No Content", + "content": {} + } + }, + "x-codegen-request-body-name": "body" + } + }, + "/groups": { + "get": { + "summary": "Fetch a list of Groups", + "description": "Without any params, returns a list of the Groups the user belongs to", + "tags": [ + "Groups" + ], + "parameters": [ + { + "name": "all", + "in": "query", + "description": "Can only be used by admins or managers to fetch all entities", + "schema": { + "type": "boolean" } - }, - "/users/{id}": { - "put": { - "summary": "Update a User", - "parameters": [ - { - "$ref": "#/parameters/entityId" - }, - { - "$ref": "#/parameters/User" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/User" - } - } - } - }, - "delete": { - "summary": "Delete a User", - "parameters": [ - { - "$ref": "#/parameters/entityId" - } - ], - "responses": { - "204": { - "description": "No Content" - } - } + }, + { + "name": "userId", + "in": "query", + "description": "Standard users can use this only with their own _userId_", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Group" + } + } + } + } + } + } + }, + "post": { + "summary": "Create a Group", + "tags": [ + "Groups" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Group" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Group" + } + } + } + }, + "400": { + "description": "No permission", + "content": {} + } + }, + "x-codegen-request-body-name": "body" + } + }, + "/groups/{id}": { + "put": { + "summary": "Update a Group", + "tags": [ + "Groups" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Group" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Group" + } + } + } + } + }, + "x-codegen-request-body-name": "body" + }, + "delete": { + "summary": "Delete a Group", + "tags": [ + "Groups" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "204": { + "description": "No Content", + "content": {} + } + } + } + }, + "/permissions": { + "post": { + "summary": "Link an Object to another Object", + "tags": [ + "Permissions" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Permission" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Permission" + } + } + } + }, + "400": { + "description": "No permission", + "content": {} + } + }, + "x-codegen-request-body-name": "body" + }, + "delete": { + "summary": "Unlink an Object from another Object", + "tags": [ + "Permissions" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Permission" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "No Content", + "content": {} + } + }, + "x-codegen-request-body-name": "body" + } + }, + "/positions": { + "get": { + "summary": "Fetches a list of Positions", + "description": "We strongly recommend using [Traccar WebSocket API](https://www.traccar.org/traccar-api/) instead of periodically polling positions endpoint. Without any params, it returns a list of last known positions for all the user's Devices. _from_ and _to_ fields are not required with _id_.", + "tags": [ + "Positions" + ], + "parameters": [ + { + "name": "deviceId", + "in": "query", + "description": "_deviceId_ is optional, but requires the _from_ and _to_ parameters when used", + "schema": { + "type": "integer" } - }, - "/notifications": { - "get": { - "summary": "Fetch a list of Notifications", - "description": "Without params, it returns a list of Notifications the user has access to", - "parameters": [ - { - "$ref": "#/parameters/all" - }, - { - "$ref": "#/parameters/userId" - }, - { - "$ref": "#/parameters/deviceId" - }, - { - "$ref": "#/parameters/groupId" - }, - { - "$ref": "#/parameters/refresh" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/Notification" - } - } - } - } - }, - "post": { - "summary": "Create a Notification", - "parameters": [ - { - "$ref": "#/parameters/Notification" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Notification" - } - } - } + }, + { + "name": "from", + "in": "query", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "schema": { + "type": "string", + "format": "date-time" } - }, - "/notifications/{id}": { - "put": { - "summary": "Update a Notification", - "parameters": [ - { - "$ref": "#/parameters/entityId" - }, - { - "$ref": "#/parameters/Notification" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Notification" - } - } - } - }, - "delete": { - "summary": "Delete a Notification", - "parameters": [ - { - "$ref": "#/parameters/entityId" - } - ], - "responses": { - "204": { - "description": "No Content" - } - } + }, + { + "name": "to", + "in": "query", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "schema": { + "type": "string", + "format": "date-time" } - }, - "/notifications/types": { - "get": { - "summary": "Fetch a list of available Notification types", - "parameters": [], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/NotificationType" - } - } - } + }, + { + "name": "id", + "in": "query", + "description": "To fetch one or more positions. Multiple params can be passed like `id=31&id=42`", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Position" + } + } + }, + "text/csv": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Position" + } + } + }, + "application/gpx+xml": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Position" + } + } + } + } + } + } + } + }, + "/server": { + "get": { + "summary": "Fetch Server information", + "tags": [ + "Server" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Server" + } + } + } + } + } + }, + "put": { + "summary": "Update Server information", + "tags": [ + "Server" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Server" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Server" + } + } + } + } + }, + "x-codegen-request-body-name": "body" + } + }, + "/session": { + "get": { + "summary": "Fetch Session information", + "tags": [ + "Session" + ], + "parameters": [ + { + "name": "token", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "404": { + "description": "Not Found", + "content": {} + } + } + }, + "post": { + "summary": "Create a new Session", + "tags": [ + "Session" + ], + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "required": [ + "email", + "password" + ], + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string", + "format": "password" + } } + } } + }, + "required": true }, - "/notifications/test": { - "post": { - "summary": "Send test notification to current user via Email and SMS", - "parameters": [], - "responses": { - "204": { - "description": "Successful sending" - }, - "400": { - "description": "Could happen if sending has failed" - } + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" } + } } - }, - "/geofences": { - "get": { - "summary": "Fetch a list of Geofences", - "description": "Without params, it returns a list of Geofences the user has access to", - "parameters": [ - { - "$ref": "#/parameters/all" - }, - { - "$ref": "#/parameters/userId" - }, - { - "$ref": "#/parameters/deviceId" - }, - { - "$ref": "#/parameters/groupId" - }, - { - "$ref": "#/parameters/refresh" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/Geofence" - } - } - } - } - }, - "post": { - "summary": "Create a Geofence", - "parameters": [ - { - "$ref": "#/parameters/Geofence" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Geofence" - } - } - } + }, + "401": { + "description": "Unauthorized", + "content": {} + } + } + }, + "delete": { + "summary": "Close the Session", + "tags": [ + "Session" + ], + "responses": { + "204": { + "description": "No Content", + "content": {} + } + } + } + }, + "/users": { + "get": { + "summary": "Fetch a list of Users", + "tags": [ + "Users" + ], + "parameters": [ + { + "name": "userId", + "in": "query", + "description": "Can only be used by admin or manager users", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/User" + } + } + } + } + }, + "400": { + "description": "No Permission", + "content": {} + } + } + }, + "post": { + "summary": "Create a User", + "tags": [ + "Users" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + } + }, + "x-codegen-request-body-name": "body" + } + }, + "/users/{id}": { + "put": { + "summary": "Update a User", + "tags": [ + "Users" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + } + }, + "x-codegen-request-body-name": "body" + }, + "delete": { + "summary": "Delete a User", + "tags": [ + "Users" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "204": { + "description": "No Content", + "content": {} + } + } + } + }, + "/notifications": { + "get": { + "summary": "Fetch a list of Notifications", + "description": "Without params, it returns a list of Notifications the user has access to", + "tags": [ + "Notifications" + ], + "parameters": [ + { + "name": "all", + "in": "query", + "description": "Can only be used by admins or managers to fetch all entities", + "schema": { + "type": "boolean" } - }, - "/geofences/{id}": { - "put": { - "summary": "Update a Geofence", - "parameters": [ - { - "$ref": "#/parameters/entityId" - }, - { - "$ref": "#/parameters/Geofence" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Geofence" - } - } - } - }, - "delete": { - "summary": "Delete a Geofence", - "parameters": [ - { - "$ref": "#/parameters/entityId" - } - ], - "responses": { - "204": { - "description": "No Content" - } - } + }, + { + "name": "userId", + "in": "query", + "description": "Standard users can use this only with their own _userId_", + "schema": { + "type": "integer" } - }, - "/events/{id}": { - "get": { - "parameters": [ - { - "$ref": "#/parameters/entityId" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Event" - } - } - } + }, + { + "name": "deviceId", + "in": "query", + "description": "Standard users can use this only with _deviceId_s, they have access to", + "schema": { + "type": "integer" } - }, - "/reports/route": { - "get": { - "summary": "Fetch a list of Positions within the time period for the Devices or Groups", - "description": "At least one _deviceId_ or one _groupId_ must be passed", - "consumes": [ - "application/json", - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" - ], - "produces": [ - "application/json", - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" - ], - "parameters": [ - { - "$ref": "#/parameters/deviceIdArray" - }, - { - "$ref": "#/parameters/groupIdArray" - }, - { - "$ref": "#/parameters/fromTime" - }, - { - "$ref": "#/parameters/toTime" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/Position" - } - } - } - } + }, + { + "name": "groupId", + "in": "query", + "description": "Standard users can use this only with _groupId_s, they have access to", + "schema": { + "type": "integer" } - }, - "/reports/events": { - "get": { - "summary": "Fetch a list of Events within the time period for the Devices or Groups", - "description": "At least one _deviceId_ or one _groupId_ must be passed", - "consumes": [ - "application/json", - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" - ], - "produces": [ - "application/json", - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" - ], - "parameters": [ - { - "$ref": "#/parameters/deviceIdArray" - }, - { - "$ref": "#/parameters/groupIdArray" - }, - { - "name": "type", - "in": "query", - "description": "% can be used to return events of all types", - "type": "array", - "items": { - "type": "string" - } - }, - { - "$ref": "#/parameters/fromTime" - }, - { - "$ref": "#/parameters/toTime" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/Event" - } - } - } - } + }, + { + "name": "refresh", + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Notification" + } + } + } + } + } + } + }, + "post": { + "summary": "Create a Notification", + "tags": [ + "Notifications" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Notification" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Notification" + } + } + } + } + }, + "x-codegen-request-body-name": "body" + } + }, + "/notifications/{id}": { + "put": { + "summary": "Update a Notification", + "tags": [ + "Notifications" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Notification" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Notification" + } + } + } + } + }, + "x-codegen-request-body-name": "body" + }, + "delete": { + "summary": "Delete a Notification", + "tags": [ + "Notifications" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "204": { + "description": "No Content", + "content": {} + } + } + } + }, + "/notifications/types": { + "get": { + "summary": "Fetch a list of available Notification types", + "tags": [ + "Notifications" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NotificationType" + } + } + } + } + } + } + } + }, + "/notifications/test": { + "post": { + "summary": "Send test notification to current user via Email and SMS", + "tags": [ + "Notifications" + ], + "responses": { + "204": { + "description": "Successful sending", + "content": {} + }, + "400": { + "description": "Could happen if sending has failed", + "content": {} + } + } + } + }, + "/geofences": { + "get": { + "summary": "Fetch a list of Geofences", + "description": "Without params, it returns a list of Geofences the user has access to", + "tags": [ + "Geofences" + ], + "parameters": [ + { + "name": "all", + "in": "query", + "description": "Can only be used by admins or managers to fetch all entities", + "schema": { + "type": "boolean" } - }, - "/reports/summary": { - "get": { - "summary": "Fetch a list of ReportSummary within the time period for the Devices or Groups", - "description": "At least one _deviceId_ or one _groupId_ must be passed", - "consumes": [ - "application/json", - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" - ], - "produces": [ - "application/json", - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" - ], - "parameters": [ - { - "$ref": "#/parameters/deviceIdArray" - }, - { - "$ref": "#/parameters/groupIdArray" - }, - { - "$ref": "#/parameters/fromTime" - }, - { - "$ref": "#/parameters/toTime" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/ReportSummary" - } - } - } - } + }, + { + "name": "userId", + "in": "query", + "description": "Standard users can use this only with their own _userId_", + "schema": { + "type": "integer" } - }, - "/reports/trips": { - "get": { - "summary": "Fetch a list of ReportTrips within the time period for the Devices or Groups", - "description": "At least one _deviceId_ or one _groupId_ must be passed", - "consumes": [ - "application/json", - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" - ], - "produces": [ - "application/json", - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" - ], - "parameters": [ - { - "$ref": "#/parameters/deviceIdArray" - }, - { - "$ref": "#/parameters/groupIdArray" - }, - { - "$ref": "#/parameters/fromTime" - }, - { - "$ref": "#/parameters/toTime" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/ReportTrips" - } - } - } - } + }, + { + "name": "deviceId", + "in": "query", + "description": "Standard users can use this only with _deviceId_s, they have access to", + "schema": { + "type": "integer" } - }, - "/reports/stops": { - "get": { - "summary": "Fetch a list of ReportStops within the time period for the Devices or Groups", - "description": "At least one _deviceId_ or one _groupId_ must be passed", - "consumes": [ - "application/json", - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" - ], - "produces": [ - "application/json", - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" - ], - "parameters": [ - { - "$ref": "#/parameters/deviceIdArray" - }, - { - "$ref": "#/parameters/groupIdArray" - }, - { - "$ref": "#/parameters/fromTime" - }, - { - "$ref": "#/parameters/toTime" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/ReportStops" - } - } - } - } + }, + { + "name": "groupId", + "in": "query", + "description": "Standard users can use this only with _groupId_s, they have access to", + "schema": { + "type": "integer" } - }, - "/statistics": { - "get": { - "summary": "Fetch server Statistics", - "parameters": [ - { - "$ref": "#/parameters/fromTime" - }, - { - "$ref": "#/parameters/toTime" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/Statistics" - } - } - } - } + }, + { + "name": "refresh", + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Geofence" + } + } + } + } + } + } + }, + "post": { + "summary": "Create a Geofence", + "tags": [ + "Geofences" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Geofence" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Geofence" + } + } + } + } + }, + "x-codegen-request-body-name": "body" + } + }, + "/geofences/{id}": { + "put": { + "summary": "Update a Geofence", + "tags": [ + "Geofences" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Geofence" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Geofence" + } + } + } + } + }, + "x-codegen-request-body-name": "body" + }, + "delete": { + "summary": "Delete a Geofence", + "tags": [ + "Geofences" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "204": { + "description": "No Content", + "content": {} + } + } + } + }, + "/events/{id}": { + "get": { + "tags": [ + "Events" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" } - }, - "/calendars": { - "get": { - "summary": "Fetch a list of Calendars", - "description": "Without params, it returns a list of Calendars the user has access to", - "parameters": [ - { - "$ref": "#/parameters/all" - }, - { - "$ref": "#/parameters/userId" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/Calendar" - } - } - } - } - }, - "post": { - "summary": "Create a Calendar", - "parameters": [ - { - "$ref": "#/parameters/Calendar" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Calendar" - } - } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Event" } + } } - }, - "/calendars/{id}": { - "put": { - "summary": "Update a Calendar", - "parameters": [ - { - "$ref": "#/parameters/entityId" - }, - { - "$ref": "#/parameters/Calendar" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Calendar" - } - } - } - }, - "delete": { - "summary": "Delete a Calendar", - "parameters": [ - { - "$ref": "#/parameters/entityId" - } - ], - "responses": { - "204": { - "description": "No Content" - } - } + } + } + } + }, + "/reports/route": { + "get": { + "summary": "Fetch a list of Positions within the time period for the Devices or Groups", + "description": "At least one _deviceId_ or one _groupId_ must be passed", + "tags": [ + "Reports" + ], + "parameters": [ + { + "name": "deviceId", + "in": "query", + "style": "form", + "explode": true, + "schema": { + "type": "array", + "items": { + "type": "integer" + } } - }, - "/attributes/computed": { - "get": { - "summary": "Fetch a list of Attributes", - "description": "Without params, it returns a list of Attributes the user has access to", - "parameters": [ - { - "$ref": "#/parameters/all" - }, - { - "$ref": "#/parameters/userId" - }, - { - "$ref": "#/parameters/deviceId" - }, - { - "$ref": "#/parameters/groupId" - }, - { - "$ref": "#/parameters/refresh" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/Attribute" - } - } - } - } - }, - "post": { - "summary": "Create an Attribute", - "parameters": [ - { - "$ref": "#/parameters/Attribute" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Attribute" - } - } - } + }, + { + "name": "groupId", + "in": "query", + "style": "form", + "explode": true, + "schema": { + "type": "array", + "items": { + "type": "integer" + } } - }, - "/attributes/computed/{id}": { - "put": { - "summary": "Update an Attribute", - "parameters": [ - { - "$ref": "#/parameters/entityId" - }, - { - "$ref": "#/parameters/Attribute" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Attribute" - } - } - } - }, - "delete": { - "summary": "Delete an Attribute", - "parameters": [ - { - "$ref": "#/parameters/entityId" - } - ], - "responses": { - "204": { - "description": "No Content" - } - } + }, + { + "name": "from", + "in": "query", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "required": true, + "schema": { + "type": "string", + "format": "date-time" } - }, - "/drivers": { - "get": { - "summary": "Fetch a list of Drivers", - "description": "Without params, it returns a list of Drivers the user has access to", - "parameters": [ - { - "$ref": "#/parameters/all" - }, - { - "$ref": "#/parameters/userId" - }, - { - "$ref": "#/parameters/deviceId" - }, - { - "$ref": "#/parameters/groupId" - }, - { - "$ref": "#/parameters/refresh" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/Driver" - } - } - } - } - }, - "post": { - "summary": "Create a Driver", - "parameters": [ - { - "$ref": "#/parameters/Driver" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Driver" - } - } - } + }, + { + "name": "to", + "in": "query", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "required": true, + "schema": { + "type": "string", + "format": "date-time" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Position" + } + } + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Position" + } + } + } + } + } + } + } + }, + "/reports/events": { + "get": { + "summary": "Fetch a list of Events within the time period for the Devices or Groups", + "description": "At least one _deviceId_ or one _groupId_ must be passed", + "tags": [ + "Reports" + ], + "parameters": [ + { + "name": "deviceId", + "in": "query", + "style": "form", + "explode": true, + "schema": { + "type": "array", + "items": { + "type": "integer" + } } - }, - "/drivers/{id}": { - "put": { - "summary": "Update a Driver", - "parameters": [ - { - "$ref": "#/parameters/entityId" - }, - { - "$ref": "#/parameters/Driver" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Driver" - } - } - } - }, - "delete": { - "summary": "Delete a Driver", - "parameters": [ - { - "$ref": "#/parameters/entityId" - } - ], - "responses": { - "204": { - "description": "No Content" - } - } + }, + { + "name": "groupId", + "in": "query", + "style": "form", + "explode": true, + "schema": { + "type": "array", + "items": { + "type": "integer" + } } - }, - "/maintenance": { - "get": { - "summary": "Fetch a list of Maintenance", - "description": "Without params, it returns a list of Maintenance the user has access to", - "parameters": [ - { - "$ref": "#/parameters/all" - }, - { - "$ref": "#/parameters/userId" - }, - { - "$ref": "#/parameters/deviceId" - }, - { - "$ref": "#/parameters/groupId" - }, - { - "$ref": "#/parameters/refresh" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/Maintenance" - } - } - } - } - }, - "post": { - "summary": "Create a Maintenance", - "parameters": [ - { - "$ref": "#/parameters/Maintenance" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Maintenance" - } - } - } + }, + { + "name": "type", + "in": "query", + "description": "% can be used to return events of all types", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "type": "string" + } } - }, - "/maintenance/{id}": { - "put": { - "summary": "Update a Maintenance", - "parameters": [ - { - "$ref": "#/parameters/entityId" - }, - { - "$ref": "#/parameters/Maintenance" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Maintenance" - } - } - } - }, - "delete": { - "summary": "Delete a Maintenance", - "parameters": [ - { - "$ref": "#/parameters/entityId" - } - ], - "responses": { - "204": { - "description": "No Content" - } - } + }, + { + "name": "from", + "in": "query", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "required": true, + "schema": { + "type": "string", + "format": "date-time" } + }, + { + "name": "to", + "in": "query", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "required": true, + "schema": { + "type": "string", + "format": "date-time" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Event" + } + } + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Event" + } + } + } + } + } } + } }, - "definitions": { - "Position": { - "properties": { - "id": { - "type": "integer" - }, - "deviceId": { - "type": "integer" - }, - "protocol": { - "type": "string" - }, - "deviceTime": { - "type": "string", - "format": "date-time", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`" - }, - "fixTime": { - "type": "string", - "format": "date-time", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`" - }, - "serverTime": { - "type": "string", - "format": "date-time", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`" - }, - "outdated": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - }, - "latitude": { - "type": "number" - }, - "longitude": { - "type": "number" - }, - "altitude": { - "type": "number" - }, - "speed": { - "type": "number", - "description": "in knots" - }, - "course": { - "type": "number" - }, - "address": { - "type": "string" - }, - "accuracy": { - "type": "number" - }, - "network": { - "type": "object", - "additionalProperties": true - }, - "attributes": { - "type": "object", - "additionalProperties": true - } - } - }, - "User": { - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "email": { - "type": "string" - }, - "phone": { - "type": "string" - }, - "readonly": { - "type": "boolean" - }, - "administrator": { - "type": "boolean" - }, - "map": { - "type": "string" - }, - "latitude": { - "type": "number" - }, - "longitude": { - "type": "number" - }, - "zoom": { - "type": "integer" - }, - "password": { - "type": "string" - }, - "twelveHourFormat": { - "type": "boolean" - }, - "coordinateFormat": { - "type": "string" - }, - "disabled": { - "type": "boolean" - }, - "expirationTime": { - "type": "string", - "format": "date-time", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`" - }, - "deviceLimit": { - "type": "integer" - }, - "userLimit": { - "type": "integer" - }, - "deviceReadonly": { - "type": "boolean" - }, - "limitCommands": { - "type": "boolean" - }, - "poiLayer": { - "type": "string" - }, - "token": { - "type": "string" - }, - "attributes": { - "type": "object", - "additionalProperties": true - } - } - }, - "Server": { - "properties": { - "id": { - "type": "integer" - }, - "registration": { - "type": "boolean" - }, - "readonly": { - "type": "boolean" - }, - "deviceReadonly": { - "type": "boolean" - }, - "limitCommands": { - "type": "boolean" - }, - "map": { - "type": "string" - }, - "bingKey": { - "type": "string" - }, - "mapUrl": { - "type": "string" - }, - "poiLayer": { - "type": "string" - }, - "latitude": { - "type": "number" - }, - "longitude": { - "type": "number" - }, - "zoom": { - "type": "integer" - }, - "twelveHourFormat": { - "type": "boolean" - }, - "version": { - "type": "string" - }, - "forceSettings": { - "type": "boolean" - }, - "coordinateFormat": { - "type": "string" - }, - "attributes": { - "type": "object", - "additionalProperties": true - } + "/reports/summary": { + "get": { + "summary": "Fetch a list of ReportSummary within the time period for the Devices or Groups", + "description": "At least one _deviceId_ or one _groupId_ must be passed", + "tags": [ + "Reports" + ], + "parameters": [ + { + "name": "deviceId", + "in": "query", + "style": "form", + "explode": true, + "schema": { + "type": "array", + "items": { + "type": "integer" + } } - }, - "Command": { - "properties": { - "id": { - "type": "integer" - }, - "deviceId": { - "type": "integer" - }, - "description": { - "type": "string" - }, - "type": { - "type": "string" - }, - "attributes": { - "type": "object", - "additionalProperties": true - } + }, + { + "name": "groupId", + "in": "query", + "style": "form", + "explode": true, + "schema": { + "type": "array", + "items": { + "type": "integer" + } } - }, - "Device": { - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "uniqueId": { - "type": "string" - }, - "status": { - "type": "string" - }, - "disabled": { - "type": "boolean" - }, - "lastUpdate": { - "type": "string", - "format": "date-time", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`" - }, - "positionId": { - "type": "integer" - }, - "groupId": { - "type": "integer" - }, - "phone": { - "type": "string" - }, - "model": { - "type": "string" - }, - "contact": { - "type": "string" - }, - "category": { - "type": "string" - }, - "geofenceIds": { - "type": "array", - "items": { - "type": "integer" - } - }, - "attributes": { - "type": "object", - "additionalProperties": true - } + }, + { + "name": "from", + "in": "query", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "required": true, + "schema": { + "type": "string", + "format": "date-time" } - }, - "Group": { - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "groupId": { - "type": "integer" - }, - "attributes": { - "type": "object", - "additionalProperties": true - } + }, + { + "name": "to", + "in": "query", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "required": true, + "schema": { + "type": "string", + "format": "date-time" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ReportSummary" + } + } + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ReportSummary" + } + } + } + } + } + } + } + }, + "/reports/trips": { + "get": { + "summary": "Fetch a list of ReportTrips within the time period for the Devices or Groups", + "description": "At least one _deviceId_ or one _groupId_ must be passed", + "tags": [ + "Reports" + ], + "parameters": [ + { + "name": "deviceId", + "in": "query", + "style": "form", + "explode": true, + "schema": { + "type": "array", + "items": { + "type": "integer" + } } - }, - "Permission": { - "description": "This is a permission map that contain two object indexes. It is used to link/unlink objects. Order is important. Example: { deviceId:8, geofenceId: 16 }", - "properties": { - "userId": { - "description": "User Id, can be only first parameter", - "type": "integer" - }, - "deviceId": { - "description": "Device Id, can be first parameter or second only in combination with userId", - "type": "integer" - }, - "groupId": { - "description": "Group Id, can be first parameter or second only in combination with userId", - "type": "integer" - }, - "geofenceId": { - "description": "Geofence Id, can be second parameter only", - "type": "integer" - }, - "calendarId": { - "description": "Calendar Id, can be second parameter only and only in combination with userId", - "type": "integer" - }, - "attributeId": { - "description": "Computed Attribute Id, can be second parameter only", - "type": "integer" - }, - "driverId": { - "description": "Driver Id, can be second parameter only", - "type": "integer" - }, - "managedUserId": { - "description": "User Id, can be second parameter only and only in combination with userId", - "type": "integer" - } + }, + { + "name": "groupId", + "in": "query", + "style": "form", + "explode": true, + "schema": { + "type": "array", + "items": { + "type": "integer" + } } - }, - "CommandType": { - "properties": { - "type": { - "type": "string" - } + }, + { + "name": "from", + "in": "query", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "required": true, + "schema": { + "type": "string", + "format": "date-time" } - }, - "Geofence": { - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "area": { - "type": "string" - }, - "calendarId": { - "type": "integer" - }, - "attributes": { - "type": "object", - "additionalProperties": true - } + }, + { + "name": "to", + "in": "query", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "required": true, + "schema": { + "type": "string", + "format": "date-time" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ReportTrips" + } + } + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ReportTrips" + } + } + } + } + } + } + } + }, + "/reports/stops": { + "get": { + "summary": "Fetch a list of ReportStops within the time period for the Devices or Groups", + "description": "At least one _deviceId_ or one _groupId_ must be passed", + "tags": [ + "Reports" + ], + "parameters": [ + { + "name": "deviceId", + "in": "query", + "style": "form", + "explode": true, + "schema": { + "type": "array", + "items": { + "type": "integer" + } } - }, - "Notification": { - "properties": { - "id": { - "type": "integer" - }, - "type": { - "type": "string" - }, - "always": { - "type": "boolean" - }, - "web": { - "type": "boolean" - }, - "mail": { - "type": "boolean" - }, - "sms": { - "type": "boolean" - }, - "calendarId": { - "type": "integer" - }, - "attributes": { - "type": "object", - "additionalProperties": true - } + }, + { + "name": "groupId", + "in": "query", + "style": "form", + "explode": true, + "schema": { + "type": "array", + "items": { + "type": "integer" + } } - }, - "NotificationType": { - "properties": { - "type": { - "type": "string" - } + }, + { + "name": "from", + "in": "query", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "required": true, + "schema": { + "type": "string", + "format": "date-time" } - }, - "Event": { - "properties": { - "id": { - "type": "integer" - }, - "type": { - "type": "string" - }, - "serverTime": { - "type": "string", - "format": "date-time", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`" - }, - "deviceId": { - "type": "integer" - }, - "positionId": { - "type": "integer" - }, - "geofenceId": { - "type": "integer" - }, - "maintenanceId": { - "type": "integer" - }, - "attributes": { - "type": "object", - "additionalProperties": true - } + }, + { + "name": "to", + "in": "query", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "required": true, + "schema": { + "type": "string", + "format": "date-time" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ReportStops" + } + } + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ReportStops" + } + } + } + } + } + } + } + }, + "/statistics": { + "get": { + "summary": "Fetch server Statistics", + "tags": [ + "Statistics" + ], + "parameters": [ + { + "name": "from", + "in": "query", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "required": true, + "schema": { + "type": "string", + "format": "date-time" } - }, - "ReportSummary": { - "properties": { - "deviceId": { - "type": "integer" - }, - "deviceName": { - "type": "string" - }, - "maxSpeed": { - "type": "number", - "description": "in knots" - }, - "averageSpeed": { - "type": "number", - "description": "in knots" - }, - "distance": { - "type": "number", - "description": "in meters" - }, - "spentFuel": { - "type": "number", - "description": "in liters" - }, - "engineHours": { - "type": "integer" - } + }, + { + "name": "to", + "in": "query", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "required": true, + "schema": { + "type": "string", + "format": "date-time" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Statistics" + } + } + } + } + } + } + } + }, + "/calendars": { + "get": { + "summary": "Fetch a list of Calendars", + "description": "Without params, it returns a list of Calendars the user has access to", + "tags": [ + "Calendars" + ], + "parameters": [ + { + "name": "all", + "in": "query", + "description": "Can only be used by admins or managers to fetch all entities", + "schema": { + "type": "boolean" } - }, - "ReportTrips": { - "properties": { - "deviceId": { - "type": "integer" - }, - "deviceName": { - "type": "string" - }, - "maxSpeed": { - "type": "number", - "description": "in knots" - }, - "averageSpeed": { - "type": "number", - "description": "in knots" - }, - "distance": { - "type": "number", - "description": "in meters" - }, - "spentFuel": { - "type": "number", - "description": "in liters" - }, - "duration": { - "type": "integer" - }, - "startTime": { - "type": "string", - "format": "date-time", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`" - }, - "startAddress": { - "type": "string" - }, - "startLat": { - "type": "number" - }, - "startLon": { - "type": "number" - }, - "endTime": { - "type": "string", - "format": "date-time", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`" - }, - "endAddress": { - "type": "string" - }, - "endLat": { - "type": "number" - }, - "endLon": { - "type": "number" - }, - "driverUniqueId": { - "type": "integer" - }, - "driverName": { - "type": "string" - } + }, + { + "name": "userId", + "in": "query", + "description": "Standard users can use this only with their own _userId_", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Calendar" + } + } + } + } + } + } + }, + "post": { + "summary": "Create a Calendar", + "tags": [ + "Calendars" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Calendar" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Calendar" + } + } + } + } + }, + "x-codegen-request-body-name": "body" + } + }, + "/calendars/{id}": { + "put": { + "summary": "Update a Calendar", + "tags": [ + "Calendars" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Calendar" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Calendar" + } + } + } + } + }, + "x-codegen-request-body-name": "body" + }, + "delete": { + "summary": "Delete a Calendar", + "tags": [ + "Calendars" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "204": { + "description": "No Content", + "content": {} + } + } + } + }, + "/attributes/computed": { + "get": { + "summary": "Fetch a list of Attributes", + "description": "Without params, it returns a list of Attributes the user has access to", + "tags": [ + "Attributes" + ], + "parameters": [ + { + "name": "all", + "in": "query", + "description": "Can only be used by admins or managers to fetch all entities", + "schema": { + "type": "boolean" } - }, - "ReportStops": { - "properties": { - "deviceId": { - "type": "integer" - }, - "deviceName": { - "type": "string" - }, - "duration": { - "type": "integer" - }, - "startTime": { - "type": "string", - "format": "date-time", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`" - }, - "address": { - "type": "string" - }, - "lat": { - "type": "number" - }, - "lon": { - "type": "number" - }, - "endTime": { - "type": "string", - "format": "date-time", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`" - }, - "spentFuel": { - "type": "number", - "description": "in liters" - }, - "engineHours": { - "type": "integer" - } + }, + { + "name": "userId", + "in": "query", + "description": "Standard users can use this only with their own _userId_", + "schema": { + "type": "integer" } - }, - "Statistics": { - "properties": { - "captureTime": { - "type": "string", - "format": "date-time", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`" - }, - "activeUsers": { - "type": "integer" - }, - "activeDevices": { - "type": "integer" - }, - "requests": { - "type": "integer" - }, - "messagesReceived": { - "type": "integer" - }, - "messagesStored": { - "type": "integer" - } + }, + { + "name": "deviceId", + "in": "query", + "description": "Standard users can use this only with _deviceId_s, they have access to", + "schema": { + "type": "integer" } - }, - "DeviceAccumulators": { - "properties": { - "deviceId": { - "type": "integer" - }, - "totalDistance": { - "type": "number", - "description": "in meters" - }, - "hours": { - "type": "number" - } + }, + { + "name": "groupId", + "in": "query", + "description": "Standard users can use this only with _groupId_s, they have access to", + "schema": { + "type": "integer" } - }, - "Calendar": { - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "data": { - "type": "string", - "description": "base64 encoded in iCalendar format" - }, - "attributes": { - "type": "object", - "additionalProperties": true - } + }, + { + "name": "refresh", + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Attribute" + } + } + } + } + } + } + }, + "post": { + "summary": "Create an Attribute", + "tags": [ + "Attributes" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Attribute" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Attribute" + } + } + } + } + }, + "x-codegen-request-body-name": "body" + } + }, + "/attributes/computed/{id}": { + "put": { + "summary": "Update an Attribute", + "tags": [ + "Attributes" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Attribute" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Attribute" + } + } + } + } + }, + "x-codegen-request-body-name": "body" + }, + "delete": { + "summary": "Delete an Attribute", + "tags": [ + "Attributes" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "204": { + "description": "No Content", + "content": {} + } + } + } + }, + "/drivers": { + "get": { + "summary": "Fetch a list of Drivers", + "description": "Without params, it returns a list of Drivers the user has access to", + "tags": [ + "Drivers" + ], + "parameters": [ + { + "name": "all", + "in": "query", + "description": "Can only be used by admins or managers to fetch all entities", + "schema": { + "type": "boolean" } - }, - "Attribute": { - "properties": { - "id": { - "type": "integer" - }, - "description": { - "type": "string" - }, - "attribute": { - "type": "string" - }, - "expression": { - "type": "string" - }, - "type": { - "type": "string", - "description": "String|Number|Boolean" - } + }, + { + "name": "userId", + "in": "query", + "description": "Standard users can use this only with their own _userId_", + "schema": { + "type": "integer" } - }, - "Driver": { - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "uniqueId": { - "type": "string" - }, - "attributes": { - "type": "object", - "additionalProperties": true - } + }, + { + "name": "deviceId", + "in": "query", + "description": "Standard users can use this only with _deviceId_s, they have access to", + "schema": { + "type": "integer" } - }, - "Maintenance": { - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "type": { - "type": "string" - }, - "start": { - "type": "number" - }, - "period": { - "type": "number" - }, - "attributes": { - "type": "object", - "additionalProperties": true - } + }, + { + "name": "groupId", + "in": "query", + "description": "Standard users can use this only with _groupId_s, they have access to", + "schema": { + "type": "integer" } + }, + { + "name": "refresh", + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Driver" + } + } + } + } + } } + }, + "post": { + "summary": "Create a Driver", + "tags": [ + "Drivers" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Driver" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Driver" + } + } + } + } + }, + "x-codegen-request-body-name": "body" + } }, - "parameters": { - "entityId": { + "/drivers/{id}": { + "put": { + "summary": "Update a Driver", + "tags": [ + "Drivers" + ], + "parameters": [ + { "name": "id", "in": "path", "required": true, - "type": "integer" - }, - "all": { + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Driver" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Driver" + } + } + } + } + }, + "x-codegen-request-body-name": "body" + }, + "delete": { + "summary": "Delete a Driver", + "tags": [ + "Drivers" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "204": { + "description": "No Content", + "content": {} + } + } + } + }, + "/maintenance": { + "get": { + "summary": "Fetch a list of Maintenance", + "description": "Without params, it returns a list of Maintenance the user has access to", + "tags": [ + "Maintenance" + ], + "parameters": [ + { "name": "all", "in": "query", "description": "Can only be used by admins or managers to fetch all entities", - "type": "boolean" - }, - "refresh": { - "name": "refresh", - "in": "query", - "required": false, - "type": "boolean" - }, - "userId": { + "schema": { + "type": "boolean" + } + }, + { "name": "userId", "in": "query", "description": "Standard users can use this only with their own _userId_", - "type": "integer" - }, - "deviceId": { + "schema": { + "type": "integer" + } + }, + { "name": "deviceId", "in": "query", "description": "Standard users can use this only with _deviceId_s, they have access to", - "type": "integer" - }, - "groupId": { + "schema": { + "type": "integer" + } + }, + { "name": "groupId", "in": "query", "description": "Standard users can use this only with _groupId_s, they have access to", - "type": "integer" - }, - "Device": { - "name": "body", - "in": "body", - "required": true, "schema": { - "$ref": "#/definitions/Device" + "type": "integer" } - }, - "Permission": { - "name": "body", - "in": "body", + }, + { + "name": "refresh", + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Maintenance" + } + } + } + } + } + } + }, + "post": { + "summary": "Create a Maintenance", + "tags": [ + "Maintenance" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Maintenance" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Maintenance" + } + } + } + } + }, + "x-codegen-request-body-name": "body" + } + }, + "/maintenance/{id}": { + "put": { + "summary": "Update a Maintenance", + "tags": [ + "Maintenance" + ], + "parameters": [ + { + "name": "id", + "in": "path", "required": true, "schema": { - "$ref": "#/definitions/Permission" - } - }, - "Group": { - "name": "body", - "in": "body", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Maintenance" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Maintenance" + } + } + } + } + }, + "x-codegen-request-body-name": "body" + }, + "delete": { + "summary": "Delete a Maintenance", + "tags": [ + "Maintenance" + ], + "parameters": [ + { + "name": "id", + "in": "path", "required": true, "schema": { - "$ref": "#/definitions/Group" + "type": "integer" + } + } + ], + "responses": { + "204": { + "description": "No Content", + "content": {} + } + } + } + } + }, + "components": { + "schemas": { + "Position": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "deviceId": { + "type": "integer" + }, + "protocol": { + "type": "string" + }, + "deviceTime": { + "type": "string", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "format": "date-time" + }, + "fixTime": { + "type": "string", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "format": "date-time" + }, + "serverTime": { + "type": "string", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "format": "date-time" + }, + "outdated": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + }, + "latitude": { + "type": "number" + }, + "longitude": { + "type": "number" + }, + "altitude": { + "type": "number" + }, + "speed": { + "type": "number", + "description": "in knots" + }, + "course": { + "type": "number" + }, + "address": { + "type": "string" + }, + "accuracy": { + "type": "number" + }, + "network": { + "type": "object", + "properties": {} + }, + "attributes": { + "type": "object", + "properties": {} + } + } + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "readonly": { + "type": "boolean" + }, + "administrator": { + "type": "boolean" + }, + "map": { + "type": "string" + }, + "latitude": { + "type": "number" + }, + "longitude": { + "type": "number" + }, + "zoom": { + "type": "integer" + }, + "password": { + "type": "string" + }, + "twelveHourFormat": { + "type": "boolean" + }, + "coordinateFormat": { + "type": "string" + }, + "disabled": { + "type": "boolean" + }, + "expirationTime": { + "type": "string", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "format": "date-time" + }, + "deviceLimit": { + "type": "integer" + }, + "userLimit": { + "type": "integer" + }, + "deviceReadonly": { + "type": "boolean" + }, + "limitCommands": { + "type": "boolean" + }, + "poiLayer": { + "type": "string" + }, + "token": { + "type": "string" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + }, + "Server": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "registration": { + "type": "boolean" + }, + "readonly": { + "type": "boolean" + }, + "deviceReadonly": { + "type": "boolean" + }, + "limitCommands": { + "type": "boolean" + }, + "map": { + "type": "string" + }, + "bingKey": { + "type": "string" + }, + "mapUrl": { + "type": "string" + }, + "poiLayer": { + "type": "string" + }, + "latitude": { + "type": "number" + }, + "longitude": { + "type": "number" + }, + "zoom": { + "type": "integer" + }, + "twelveHourFormat": { + "type": "boolean" + }, + "version": { + "type": "string" + }, + "forceSettings": { + "type": "boolean" + }, + "coordinateFormat": { + "type": "string" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + }, + "Command": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "deviceId": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "type": { + "type": "string" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + }, + "Device": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "uniqueId": { + "type": "string" + }, + "status": { + "type": "string" + }, + "disabled": { + "type": "boolean" + }, + "lastUpdate": { + "type": "string", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "format": "date-time" + }, + "positionId": { + "type": "integer" + }, + "groupId": { + "type": "integer" + }, + "phone": { + "type": "string" + }, + "model": { + "type": "string" + }, + "contact": { + "type": "string" + }, + "category": { + "type": "string" + }, + "geofenceIds": { + "type": "array", + "items": { + "type": "integer" } - }, - "User": { - "name": "body", - "in": "body", - "required": true, + }, + "attributes": { + "type": "object", + "properties": {} + } + } + }, + "Group": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "integer" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + }, + "Permission": { + "type": "object", + "properties": { + "userId": { + "type": "integer", + "description": "User Id, can be only first parameter" + }, + "deviceId": { + "type": "integer", + "description": "Device Id, can be first parameter or second only in combination with userId" + }, + "groupId": { + "type": "integer", + "description": "Group Id, can be first parameter or second only in combination with userId" + }, + "geofenceId": { + "type": "integer", + "description": "Geofence Id, can be second parameter only" + }, + "calendarId": { + "type": "integer", + "description": "Calendar Id, can be second parameter only and only in combination with userId" + }, + "attributeId": { + "type": "integer", + "description": "Computed Attribute Id, can be second parameter only" + }, + "driverId": { + "type": "integer", + "description": "Driver Id, can be second parameter only" + }, + "managedUserId": { + "type": "integer", + "description": "User Id, can be second parameter only and only in combination with userId" + } + }, + "description": "This is a permission map that contain two object indexes. It is used to link/unlink objects. Order is important. Example: { deviceId:8, geofenceId: 16 }" + }, + "CommandType": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + } + }, + "Geofence": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "area": { + "type": "string" + }, + "calendarId": { + "type": "integer" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + }, + "Notification": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "always": { + "type": "boolean" + }, + "web": { + "type": "boolean" + }, + "mail": { + "type": "boolean" + }, + "sms": { + "type": "boolean" + }, + "calendarId": { + "type": "integer" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + }, + "NotificationType": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + } + }, + "Event": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "serverTime": { + "type": "string", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "format": "date-time" + }, + "deviceId": { + "type": "integer" + }, + "positionId": { + "type": "integer" + }, + "geofenceId": { + "type": "integer" + }, + "maintenanceId": { + "type": "integer" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + }, + "ReportSummary": { + "type": "object", + "properties": { + "deviceId": { + "type": "integer" + }, + "deviceName": { + "type": "string" + }, + "maxSpeed": { + "type": "number", + "description": "in knots" + }, + "averageSpeed": { + "type": "number", + "description": "in knots" + }, + "distance": { + "type": "number", + "description": "in meters" + }, + "spentFuel": { + "type": "number", + "description": "in liters" + }, + "engineHours": { + "type": "integer" + } + } + }, + "ReportTrips": { + "type": "object", + "properties": { + "deviceId": { + "type": "integer" + }, + "deviceName": { + "type": "string" + }, + "maxSpeed": { + "type": "number", + "description": "in knots" + }, + "averageSpeed": { + "type": "number", + "description": "in knots" + }, + "distance": { + "type": "number", + "description": "in meters" + }, + "spentFuel": { + "type": "number", + "description": "in liters" + }, + "duration": { + "type": "integer" + }, + "startTime": { + "type": "string", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "format": "date-time" + }, + "startAddress": { + "type": "string" + }, + "startLat": { + "type": "number" + }, + "startLon": { + "type": "number" + }, + "endTime": { + "type": "string", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "format": "date-time" + }, + "endAddress": { + "type": "string" + }, + "endLat": { + "type": "number" + }, + "endLon": { + "type": "number" + }, + "driverUniqueId": { + "type": "integer" + }, + "driverName": { + "type": "string" + } + } + }, + "ReportStops": { + "type": "object", + "properties": { + "deviceId": { + "type": "integer" + }, + "deviceName": { + "type": "string" + }, + "duration": { + "type": "integer" + }, + "startTime": { + "type": "string", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "format": "date-time" + }, + "address": { + "type": "string" + }, + "lat": { + "type": "number" + }, + "lon": { + "type": "number" + }, + "endTime": { + "type": "string", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "format": "date-time" + }, + "spentFuel": { + "type": "number", + "description": "in liters" + }, + "engineHours": { + "type": "integer" + } + } + }, + "Statistics": { + "type": "object", + "properties": { + "captureTime": { + "type": "string", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "format": "date-time" + }, + "activeUsers": { + "type": "integer" + }, + "activeDevices": { + "type": "integer" + }, + "requests": { + "type": "integer" + }, + "messagesReceived": { + "type": "integer" + }, + "messagesStored": { + "type": "integer" + } + } + }, + "DeviceAccumulators": { + "type": "object", + "properties": { + "deviceId": { + "type": "integer" + }, + "totalDistance": { + "type": "number", + "description": "in meters" + }, + "hours": { + "type": "number" + } + } + }, + "Calendar": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "data": { + "type": "string", + "description": "base64 encoded in iCalendar format" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + }, + "Attribute": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "attribute": { + "type": "string" + }, + "expression": { + "type": "string" + }, + "type": { + "type": "string", + "description": "String|Number|Boolean" + } + } + }, + "Driver": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "uniqueId": { + "type": "string" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + }, + "Maintenance": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "type": { + "type": "string" + }, + "start": { + "type": "number" + }, + "period": { + "type": "number" + }, + "attributes": { + "type": "object", + "properties": {} + } + } + } + }, + "parameters": { + "entityId": { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + }, + "all": { + "name": "all", + "in": "query", + "description": "Can only be used by admins or managers to fetch all entities", + "schema": { + "type": "boolean" + } + }, + "refresh": { + "name": "refresh", + "in": "query", + "schema": { + "type": "boolean" + } + }, + "userId": { + "name": "userId", + "in": "query", + "description": "Standard users can use this only with their own _userId_", + "schema": { + "type": "integer" + } + }, + "deviceId": { + "name": "deviceId", + "in": "query", + "description": "Standard users can use this only with _deviceId_s, they have access to", + "schema": { + "type": "integer" + } + }, + "groupId": { + "name": "groupId", + "in": "query", + "description": "Standard users can use this only with _groupId_s, they have access to", + "schema": { + "type": "integer" + } + }, + "deviceIdArray": { + "name": "deviceId", + "in": "query", + "style": "form", + "explode": true, + "schema": { + "type": "array", + "items": { + "type": "integer" + } + } + }, + "groupIdArray": { + "name": "groupId", + "in": "query", + "style": "form", + "explode": true, + "schema": { + "type": "array", + "items": { + "type": "integer" + } + } + }, + "fromTime": { + "name": "from", + "in": "query", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "required": true, + "schema": { + "type": "string", + "format": "date-time" + } + }, + "toTime": { + "name": "to", + "in": "query", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "required": true, + "schema": { + "type": "string", + "format": "date-time" + } + } + }, + "requestBodies": { + "Device": { + "content": { + "application/json": { "schema": { - "$ref": "#/definitions/User" + "$ref": "#/components/schemas/Device" } + } }, - "Geofence": { - "name": "body", - "in": "body", - "required": true, + "required": true + }, + "Permission": { + "content": { + "application/json": { "schema": { - "$ref": "#/definitions/Geofence" + "$ref": "#/components/schemas/Permission" } + } }, - "Calendar": { - "name": "body", - "in": "body", - "required": true, + "required": true + }, + "Group": { + "content": { + "application/json": { "schema": { - "$ref": "#/definitions/Calendar" + "$ref": "#/components/schemas/Group" } + } }, - "Attribute": { - "name": "body", - "in": "body", - "required": true, + "required": true + }, + "User": { + "content": { + "application/json": { "schema": { - "$ref": "#/definitions/Attribute" + "$ref": "#/components/schemas/User" } + } }, - "Driver": { - "name": "body", - "in": "body", - "required": true, + "required": true + }, + "Geofence": { + "content": { + "application/json": { "schema": { - "$ref": "#/definitions/Driver" + "$ref": "#/components/schemas/Geofence" } + } }, - "Command": { - "name": "body", - "in": "body", - "required": true, + "required": true + }, + "Calendar": { + "content": { + "application/json": { "schema": { - "$ref": "#/definitions/Command" + "$ref": "#/components/schemas/Calendar" } + } }, - "Notification": { - "name": "body", - "in": "body", - "required": true, + "required": true + }, + "Attribute": { + "content": { + "application/json": { "schema": { - "$ref": "#/definitions/Notification" + "$ref": "#/components/schemas/Attribute" } + } }, - "Maintenance": { - "name": "body", - "in": "body", - "required": true, + "required": true + }, + "Driver": { + "content": { + "application/json": { "schema": { - "$ref": "#/definitions/Maintenance" + "$ref": "#/components/schemas/Driver" } + } }, - "deviceIdArray": { - "name": "deviceId", - "in": "query", - "type": "array", - "items": { - "type": "integer" - }, - "collectionFormat": "multi" + "required": true + }, + "Command": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Command" + } + } }, - "groupIdArray": { - "name": "groupId", - "in": "query", - "type": "array", - "items": { - "type": "integer" - }, - "collectionFormat": "multi" + "required": true + }, + "Notification": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Notification" + } + } }, - "fromTime": { - "name": "from", - "in": "query", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "required": true, - "type": "string", - "format": "date-time" + "required": true + }, + "Maintenance": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Maintenance" + } + } }, - "toTime": { - "name": "to", - "in": "query", - "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", - "required": true, - "type": "string", - "format": "date-time" - } + "required": true + } }, - "securityDefinitions": { - "basicAuth": { - "type": "basic", - "description": "Basic HTTP authorization with _email_ and _password_" - } + "securitySchemes": { + "basicAuth": { + "type": "http", + "description": "Basic HTTP authorization with _email_ and _password_", + "scheme": "basic" + } } -} + } +}
\ No newline at end of file diff --git a/templates/export/summary.xlsx b/templates/export/summary.xlsx Binary files differindex be16e8931..3fcf461ff 100644 --- a/templates/export/summary.xlsx +++ b/templates/export/summary.xlsx diff --git a/templates/full/deviceInactive.vm b/templates/full/deviceInactive.vm new file mode 100644 index 000000000..51aead653 --- /dev/null +++ b/templates/full/deviceInactive.vm @@ -0,0 +1,12 @@ +#set($subject = "$device.name: inactive") +#set($lastUpdate = $dateTool.getDate()) +#set($ignore = $lastUpdate.setTime($event.getLong("lastUpdate"))) +<!DOCTYPE html> +<html> +<body> +Device: $device.name<br> +Inactive<br> +Last Update: $dateTool.format("YYYY-MM-dd HH:mm:ss", $lastUpdate, $locale, $timezone)<br> +Link: <a href="$webUrl?eventId=$event.id">$webUrl?eventId=$event.id</a> +</body> +</html> diff --git a/templates/short/deviceInactive.vm b/templates/short/deviceInactive.vm new file mode 100644 index 000000000..d7431124c --- /dev/null +++ b/templates/short/deviceInactive.vm @@ -0,0 +1,3 @@ +#set($lastUpdate = $dateTool.getDate()) +#set($ignore = $lastUpdate.setTime($event.getLong("lastUpdate"))) +$device.name inactive from $dateTool.format("YYYY-MM-dd HH:mm:ss", $lastUpdate, $locale, $timezone) diff --git a/tools/test-map.py b/tools/test-map.py new file mode 100755 index 000000000..0568b283e --- /dev/null +++ b/tools/test-map.py @@ -0,0 +1,39 @@ +#!/usr/bin/python + +import urllib +import urllib2 +import httplib +import random +import json + +server = 'localhost:5055' +baseUrl = 'http://localhost:8082' +user = { 'email' : 'admin', 'password' : 'admin' } +devices = 500 + +def login(): + request = urllib2.Request(baseUrl + '/api/session') + response = urllib2.urlopen(request, urllib.urlencode(user)) + return response.headers.get('Set-Cookie') + +def add_device(cookie, unique_id): + request = urllib2.Request(baseUrl + '/api/devices') + request.add_header('Cookie', cookie) + request.add_header('Content-Type', 'application/json') + device = { 'name' : unique_id, 'uniqueId' : unique_id } + response = urllib2.urlopen(request, json.dumps(device)) + data = json.load(response) + return data['id'] + +def send_message(conn, device_id): + params = (('id', device_id), ('lat', random.uniform(59, 61)), ('lon', random.uniform(29, 31))) + conn.request('GET', '?' + urllib.urlencode(params)) + conn.getresponse().read() + +cookie = login() +conn = httplib.HTTPConnection(server) + +for i in range(devices): + device_id = "{0:0>6}".format(i) + add_device(cookie, device_id) + send_message(conn, device_id) diff --git a/tools/test-photo.sh b/tools/test-photo.sh new file mode 100755 index 000000000..2333462fe --- /dev/null +++ b/tools/test-photo.sh @@ -0,0 +1,103 @@ +#!/bin/bash + +echo "\ +imei:123456789012345,vt10,200729133914,,F,163910.000,A,1556.7517,S,04816.3609,W,0.00,0,,0,0,0.00%,,;\ +\ +imei:123456789012345,vr,0000fa01ffd8ffdb008400140e0f120f0d14121012171514181e32211e1c1c1e3d2c2e243249404c4b47404645505a73\ +6250556d5645466488656d777b8182814e608d978c7d96737e817c011517171e1a1e3b21213b7c5346537c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c\ +7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7cffc000110800f0014003012100021101031101ffdd0004000affc401\ +a20000010501010101010100000000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613\ +516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a\ +636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9ca\ +d2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa0100030101010101010101010000000000000102030405060708090a0b1100\ +020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718\ +191a262728292a35363738393a434445464748494a535455565758595a636465666768696a7374757677b800;\ +\ +imei:123456789012345,vr,0100fa0178797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6\ +c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00956d5f80cd1423a61064ff004159fa\ +c466dee1156591a374cf27a9efd29682bdca4b8c715a1a53fefdd3fbe9fa8a064dae2efb68e51fc2ff00a115cbcff2bb81c60e7eb4090d0369c9f5e2\ +af698fbe39e1f4f9c7f23fd28296e3255c3551957121f7e6921c86d771a34be7e996ec79fdded3f871fd29b24c7d394c5777307a0603f03ffd7a76ab\ +692dd3c5e426e3cfb003aff5a4808adf46ba5911d9e25dac0e324f4fc2ac368aaf2bbbcc7e6627013ffaf4c0963d2a18d1977390d8cf23b7e1532d9c\ +29d327f134800da5b93b8c48c7d5941fe74e10c43eec4a3e831480ffd076c03f871f8546d2449f79e35fab015170233796c3fe5bc5f830a8db50b51f\ +f2d81fa293fd28018753b61fc6c7e88698755b7ecb29ff00808ff1a0086e7538e6b592108ff38e09c71fad665030abda71f95fea29302f8a5a918545\ +2cc1320e455455c96c8bce53de992b0f971eb4dab05cb9b432f22ab358c6c49dcc09fc6a53b1427d94a0e1f77d4544c197b552607fffd1e7ae16f2d6\ +ee17b994b9dc18306dc3ad6ceabfbeb48a65e763633ec6908cf4e953d9c9e55d46dd006c1fa74a066bdf1300;\ +\ +imei:123456789012345,vr,0200fa01279963327521491f8735ca4c06fde7b8a0457639356b4d5916ed1846c54fca78ec681962e930c7eb4965a67f\ +6848c3cc09b067eb528b91a917876d53991ddff4ad5b4863b5458a25da80f4a641893e2dbc40c58855639c9e0722af0bcb4857e6b88c9031f29ddfca\ +9011beb168bf74c8ff00eea7f8e2a07d6e3fe081cffbcc07f8d016216d6a53f7608c7d589ff0a85b55ba6e8635ff00753fc7340ec42d7f76dd6761f4\ +c0fe55134f337de9a53f5734058fffd2e648cf5e7eb49803b0a8285a4a041450025140055dd3ba49f87f5a4f602f8a5a918b475e0d0046d6f13758d7\ +f0e3f9545f64404e198fa67b55733158b038141a918d34d650c3045007ffd3ce88c1716eb02c32b96c12c4e769f5000ad2b7b391f44b88e61fbc5538\ +f7c5240cc446cad293e94867450c826891fb3a827f91aa29e1f83acd2349ec3814c45c8b4cb487ee40b9f53cd4af35bdbf0f2c51fb640fd280306fc2\ +33978c8646e411dea0b1bb6b3959d1431c6307bff9c549a3d8b126b176ff0074c71ffba9fe39aad25ddccbf7ee253ec1b03f4a08b107d692800a4a06\ +2514084c8a72c6eff7119bfdd526819fffd4c15b2ba6e96f2fe2b8fe7522e9776dff002c82fd5c540c9068f707ef3c43f127fa53d7466fe29c0fa2e7\ +fad30b8f1a347fc5339fa2814f1a45b8ead29fc47f8501717fb32d4750c7eaf4e163663fe59afe2c7fc64000;\ +\ +imei:123456789012345,vr,0300fa01815c5169663fe5947fcea65b583cb3e4a229ff00645017212bb4e08c1a4c56650b450014845001da92801292\ +803fffd5a9a25bc97b68ca6f658a389b1e5c6003cf39cfe75bf676f1dac7e4a17287392edb8f3484ce4a58fc99e58bfb8e47eb4dcd228bf6baa0b6b7\ +58fca2eea4e0eec0c5365d66e9fee6c8c7fb2b93fad20b1524b99e6ff5b348c3d0b71f9543803a0a00b511f32d4af743fa1ff26a9b8c31a45f417b50\ +39381c9f6a6497ed34a6b88f7c8ed17cd8c18f9faf5a6269178e065153fde71fd29d857265d0e53f7e68d7e809ff000a997438c7df9dcffbaa07f8d1\ +60b922e93669f795dbfde7ff000c5482d2ca3e90c5f88ddfce815c786863fb8aabfeeae283723fda3401ffd60dc7a2feb4c33b7602a406999fdbf2a6\ +99643fc46801bbdcf563f9d34e7d680130693140062a48a428d91480b4e8b3a065fbdfe78aaa460e0d2921a0a2a461450014868010d36803ffd7c3d3\ +6fa6b277f2481e60c1c8cf4ff26ac4b7d7537fac9dc8f40703f4a91d8af494804a33400f8ede697fd5c523fd149ab51e9178fd63083fdb6029d80b49\ +a5496d148ef2ab7cbcaa83fceb2675c31a4cb4ee8d9d2e0b47b2479228da404862c33dff00c315a0b24518c46028f455c5519b2379b2e08ce31d29a6\ +76ec050218d2b9ef8a61673d58fe7486369314c04c5262901fffd08b1498a90131498a0031498a004c51f100;\ +\ +imei:123456789012345,vr,0400fa0140071450049148636e3a77153cb109577a7defe740157a1e68acca0a2800a4a004349401ffd1e601c10476ab\ +91ab48711ab37d066a4a2d47a5ddc9ff002cb60f5738ab71e84c7fd6cea3d95734ec2b965347b48fefef7ff79b03f4a9d22b583fd5c51a9f50b93f9d\ +02b8f3703b02698676ec00a6218d23b0209e0f06b06f23d9211ef52cb816749933be33f51fe7f2ad2c5084f713146298842293140094d3480292803f\ +ffd28e92a404c518a004c518a004c518a004c518a002a6865d8707ee9a009278770de9d7f9d55a990d051523128a004a43401fffd3e63bd74da35d86\ +d3d131f3464a1f7ee3f43fa54a1b2e99dbb002986473dea8919c9eb4628017145002566ea51f3b877a4cb8ee54d3e4f2aed33d09c7e75b99a4852dc4\ +a4a6210d262800c526280131462901ffd44c5262a404c518a004c518a004c518a004c518a006e28a00b104b8f958f1da92e21c12ea38ef49ec08af45\ +41421a4a004a2803ffd5e66b4b459765cb447a48bc7d47ff005b3528a66dd1544066933400b9a4a004c541791ee84fb52635b98adf23d6fc6de646ae\ +3f8803491531d8a5c5324691498a003146290098a4a00fffd628a9010d250025140094940094500252500156a09770dadd7f9d00453c3b3e65fba7f4\ +a82a1e834069290c4a4a00ffd7e645490c86195245ea841fad4947500860194e548c83ed455102e28c501f00;\ +\ +imei:123456789012345,vr,0500fa01018a2800a465dea57d462819cf5d82ae78e6b5f4b62f61193db23f5a94548b54532469346680128a40252628\ +03ffd05c5262a4031498a004c518a004c518a004c52500262928012941c1c8a00b71b895083d7bd559a2f2dbd41e9498223a4a8284349401ffd1e616\ +9d5251d0693379b64aa4fcd19da7e9dbfcfb55dc5510c28a0028cd002668a00864b58656cc8993f5c548aa1142a80aa3a014877b8b4500368c5200c5\ +18a004c518a00fffd27628a9013145002518a004c5250021a4a004a2900869b4c0723146c8ab5f2cb1fb1fd280293a1462a69b599421a4a00fffd3e6\ +3bd3aa4b343479bcbba319e928c7e2391fd6b7334d10c4a5a620a2818514005148028a006d2d200a4a003149401fffd47d15201486801292800a4a00\ +4a43400da4a004a4a002a4864d8dcf4a009a68c489c751d2a91c8383c54c868434d35233ffd5e629c3a5496391ca3aba9c329047d6ba88dc4b1ac8bd\ +1c0229a258ea2992145030a5a0028a40251da80129690094500149401fffd69292a40292801292800a4a004a4a004c5250021a6d00251480b1049fc2\ +7f0a4b98b237af5ef4dea80a94959947ffd7e60d28a92c756de8d36fb7688f58ce47d0ff00f5f342259a145512145030a2900533ccc9381d2800dcde\ +d4d2cdeb4809051400514005262803ffd093145480525002628a004a4a00434940094868010d34d002524300;\ +\ +imei:123456789012345,vr,0600fa0150000e2adc526f5e7af7a0456b88b6364743505432d1ffd1e69873495258eea2ad69d379176849c2b7cadf43\ +ff00d7c5023a1a4aa2028a062d14804638526a34e05003b04ff09a42ad9048c5003c52d200a2800a4a00ffd29a9315201494009450021a4a004a4a00\ +4a4a0069a43400d34940094e47dac08a00b471227b1aa32214620d292047ffd3e7e45c13ed50915058a29d4c0e8ace6f3ed9243f7b186fa8a9ea880a\ +5a0029690051400521a0029690052500145007ffd49a8a90128a004a4a000d3680128a006d21a0069a43400d3486801b4669012c326d383d0d493209\ +17dc5303ffd5c9bd87ca998763d2a830c1a82c4069e28034f479b0ef093c30dc3ea3aff9f6ad6aa4430a5a602d148028a005a4340051400514804a28\ +03ffd69a8a90128a004a4a004a4a004a28010d34d0021a69a0069a69a00434da4019ab3149b860f514c0ffd7835087cc8b7e395fe558b28c66a5ee52\ +d884d3d0e45032686430cc922ff09cfd6ba30430054e41e41a68963a8a62168a0028a402d277a0028a0028a40251401fffd09a8a90129280128a004a\ +28012928010d368010d34d0034d34d0034d34d201334aafb4e45007fffd179c1183c8358d7b6fe5b9c0e3b54b1c4cf618a1786a064a2b6f4c9bccb50\ +84fcd19dbf876ff3ed42065ca5aa2428a402d1400b486800a2800a4a40145007ffd29a92a404a2801292be00;\ +\ +imei:123456789012345,vr,0700fa01800a4a0029a6801292801a4d34d00349a693400d34841a40262908a00fffd35a82f2112c44f714982dcc5953\ +150f439a945b241d2ae69b379574a09f95fe53fd29899b94b544851400b45002d21a40252d00149480292803ffd49a92a404a4a0029280129280109a\ +4a004e68c1a006914981400d2290d0034d34d002534d007fffd55a2811937d079721c743d2a830f6a834043c5482981d05a4de7dbab9fbdd1beb5353\ +205a298052d0014948028a00293348033499a00fffd6949a4cd4809460d00260d18a00314981400521a004a4a00434d3400d34d3400d3484500260d2\ +6d3401ffd72968110dd442588fa8e958b22e09a97b96b6210706a5140cd0d2a6db2b447a38c8fa8ad5cd344b173466988334b9a4014940073460d200\ +c518a6018a31480fffd09a8a9012834009494005250025250021a4a004c1a4da68010ad34ad0026d1487028010914c6902f5e3eb401fffd14cd2d021\ +6b32fa0d8f91d0f349951339c60d0add052289918a3865e194e457411b09635917a30cd08963f6d18aa10b8a5a4014500252d0025140051480ffd29a\ +8a9012834009460d001b4d1b0d001b3de8d82800da3d2931400d3814d26802a35d824855ce0f5cf5a8dae1cf4c0fd6818b14a4921dbaf4a909a04319\ +d47522abcb26fe074fe7480fffd35a514085a8ae6212c44771d28608c399307a5573c1a93464e0e466b59d00;\ +\ +imei:123456789012345,vr,0800fa017499b2ad09edf32fd3bd084cd1a2a89168a00292900514005140060d2ed3480fffd4b1b0d2eca900d946d140\ +060518a0038a692280137534b50034b1a6b13eb400de7bd34d005436cca3e5607ebc546d1385dc474a0647494806d14c0fffd55a70a0414b40199a8d\ +bed6de0706b2dd706a0d3742c67b558b794c132483f84f23d477a6074406e00af208c834bb0d5102f967da97cbf7a401e5fbd2f962800d829768f4a0\ +0303d28a4014991401ffd6b7b8526ea90137526ea004cd26680133499a004cd349a004269a680109a69a0069a69e7ad0046d12375503e9c544d6e3f8\ +588fad0030dbbe382a6a368dd4f2a7f0e6819fffd7b3b052841e94085da3d29703d28022b8844d0b2639ed5cedc45b1cad4b2e3b158706a61cf340cd\ +dd22e3ccb631b1f9a33c7d2afee14d12c4dc28dc281016a4df4009b8d1b8d0026e3eb46680133499a00fffd09f346690099a334804cd266980949400\ +9494008690d0034d34d002536801b486900869a6803fffd19f71a371a042ee346ea00375676a56f9fde28ebd7eb49951dcc79170688dbb5228b9653f\ +d9ee55cfdd3c37d2b7f34d12c4cd19a62026933400668cd002668cd200a4a00fffd29a92900525001494009494009450036928010d34d0021a69a402\ +1a69a00434da00ffd3928a042e68a005a4750ea55ba1a00c3bb80c6c41154bee9a8342653919adcd3e7f1100;\ +\ +imei:123456789012345,vr,09005a013adc027e74f94ff434d0996a8aa244349400b4500251480292803fffd4969290051400945002525002525002\ +525002534d0034d250021a6d20129b401fffd57d2d020a5a00296802adf40248f701cafea2b0e54c1a97b96b61a8db4d5dd3e6315da0ed21da7f1a01\ +9b94b54488692800a2800a4a40145007ffd6928a402514005250025250021a4a004a434009486801b4940094d3480434d3401fffd7928a042d140052\ +d001591a85bf96f951f29e949951331860d58b23bae601dc48bfce90d9d1d15448869280168a40251400945007ffd092929005250025140094500368\ +a00434da004a4a004a43400da426900da43401ffd1968a041450014b4005473c4268ca1fc3da8030aead658db0636fa81906a6d32ce5fb524af1958d\ +39cb0c64f6a455cdca298869a2800a280128a00292901fffd27d266900525001494009494005250025266801b9a42680133499a00434d34804a4a00f\ +ffd92c00;\ +" | nc "localhost" "5001" |