com.geotab.model.entity.device.Device Maven / Gradle / Ivy
/*
*
* 2020 Copyright (C) Geotab Inc. All rights reserved.
*/
package com.geotab.model.entity.device;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.geotab.model.entity.NameEntityWithVersion;
import com.geotab.model.entity.group.CompanyGroup;
import com.geotab.model.entity.group.Group;
import com.geotab.model.entity.worktime.WorkTime;
import com.geotab.model.entity.worktime.WorkTimeStandardHours;
import com.geotab.model.enumeration.DevicePlan;
import com.geotab.model.enumeration.DeviceType;
import com.geotab.model.serialization.DeviceDeserializer;
import com.geotab.util.ThirdPartyHelper;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
/**
* A Device represents the physical tracking device installed in the vehicle. A device and vehicle
* is typically synonymous since the GO tracking device is installed in a vehicle. In the case where
* there is no device; this is represented by "NoDeviceId".
*
* The device types that are supported are:
*
* -
* {@link Go9}
*
* -
* {@link Go8}
*
* -
* {@link Go7}
*
* -
* {@link Go6}
*
* -
* {@link Go5}
*
* -
* {@link Go4v3}
*
* -
* {@link CustomDevice}
*
* -
* {@link CustomVehicleDevice}
*
*
*/
@NoArgsConstructor
@Getter
@Setter
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@JsonDeserialize(using = DeviceDeserializer.class)
public class Device extends NameEntityWithVersion {
public static final Float DEFAULT_MAX_NO_LOG_SECONDS = 200.0f;
public static final LocalDateTime MAX_DATE = LocalDateTime
.of(2050, 1, 1, 0, 0, 0, 0);
public static final LocalDateTime MIN_DATE = LocalDateTime
.of(1986, 1, 1, 0, 0, 0, 0);
/**
* The default serial number of historic devices.
*/
public static final String HISTORIC_SERIAL_NUMBER = "000-000-0000";
/**
* The minimum id value for devices that are archived.
*/
public static final int HISTORIC_MIN = 1000000;
/**
* The maximum id value for devices that are archived.
*/
public static final int HISTORIC_MAX = 10000000;
/**
* The default product Id for third-party devices.
*/
public static final int CUSTOM_DEVICE_PRODUCT_ID = 10000;
/**
* The default product Id for GoDrive mobile devices.
*/
public static final int GO_DRIVE_PRODUCT_ID = 256;
/**
* The default serial number prefix for GoDrive mobile devices.
*/
public static final String GO_DRIVE_PREFIX = "GD";
/**
* The default product Id for Go2 devices.
*/
public static final int GO2_PRODUCT_ID = 1;
/**
* The default serial number prefix for Go2 devices.
*/
public static final String GO2_PREFIX = "GT";
/**
* The default product Id for Go3 devices.
*/
public static final int GO3_PRODUCT_ID = 64;
/**
* The default serial number prefix for Go3 devices.
*/
public static final String GO3_PREFIX = "G3";
/**
* The default product Id for Go4 devices (pre Go4v3).
*/
public static final int GO4_PRODUCT_ID = 65;
/**
* The default serial number prefix for Go4 devices (pre Go4v3).
*/
public static final String GO4_PREFIX = "G4";
/**
* The default product Id for Go4v3 devices.
*/
public static final int GO4_V3_PRODUCT_ID = 81;
/**
* The default serial number prefix for Go4v3 devices.
*/
public static final String GO4_V3_PREFIX = "GV";
/**
* The default product Id for Go5 devices.
*/
public static final int GO5_PRODUCT_ID = 90;
/**
* The default serial number prefix for Go5 devices.
*/
public static final String GO5_PREFIX = "G5";
/**
* The default Product Id for Go6 devices.
*/
public static final int GO6_PRODUCT_ID = 101;
/**
* The default serial number prefix for Go6 devices.
*/
public static final String GO6_PREFIX = "G6";
/**
* The default Product Id for Go7 devices.
*/
public static final int GO7_PRODUCT_ID = 105;
/**
* The default serial number prefix for Go7 devices.
*/
public static final String GO7_PREFIX = "G7";
/**
* The default Product Id for Go8 devices.
*/
public static final int GO8_PRODUCT_ID = 114;
/**
* The default serial number prefix for Go8 devices.
*/
public static final String GO8_PREFIX = "G8";
/**
* The default Product Id for Go8 devices without a gps.
*/
public static final int GO8_PRODUCT_ID_NO_GPS = 116;
/**
* The default Product Id for Go9 devices.
*/
public static final int GO9_PRODUCT_ID = 120;
/**
* The default serial number prefix for Go9 devices.
*/
public static final String GO9_PREFIX = "G9";
/**
* The default Product Id for Go9 devices without a gps.
*/
public static final int GO9_PRODUCT_ID_NO_GPS = 121;
/**
* The maximum Product Id for Go devices.
*/
public static final int GO_MAX_PRODUCT_ID = 255;
/**
* The default Product Id for untracked assets.
*/
public static final int UNTRACKED_ASSET_PRODUCT_ID = -1;
/**
* The default Hardware Id for untracked assets.
*/
public static final int UNTRACKED_ASSET_HARDWARE_ID = -1;
/**
* Prefix to product id mapping.
*/
public static final Map GO_PREFIX_TO_PRODUCT_ID_MAPPING =
ImmutableMap.builder()
.put(GO2_PREFIX, GO2_PRODUCT_ID)
.put(GO3_PREFIX, GO3_PRODUCT_ID)
.put(GO4_PREFIX, GO4_PRODUCT_ID)
.put(GO4_V3_PREFIX, GO4_V3_PRODUCT_ID)
.put(GO5_PREFIX, GO5_PRODUCT_ID)
.put(GO6_PREFIX, GO6_PRODUCT_ID)
.put(GO7_PREFIX, GO7_PRODUCT_ID)
.put(GO8_PREFIX, GO8_PRODUCT_ID)
.put(GO9_PREFIX, GO9_PRODUCT_ID)
.put(GO_DRIVE_PREFIX, GO_DRIVE_PRODUCT_ID)
.build();
// Device data
/**
* Specifies the GO or Custom {@link DeviceType}.
*/
protected DeviceType deviceType;
/**
* The date that tells the system at what moment should it start checking the device status and
* warn if there is no new data. Used when a new device is just installed or the vehicle undergoes
* a lengthy service. Default [MinDate].
*/
protected LocalDateTime ignoreDownloadsUntil;
/**
* The Serial Number of the device. Maximum length [12].
*/
protected String serialNumber;
/**
* The product id. Each device is assigned a unique hardware product id.
*/
protected Integer productId;
// FW Version
/**
* Gets or sets the expected time between downloads, i.e. how frequently the device is expected to
* produce new data. For passive devices this might range from 1 to a few days. For live devices
* this should be maximum 24h unless vehicles travel out of coverage frequently. This is used to
* check if the device is in good state.
*/
protected Duration timeToDownload;
/**
* The {@link WorkTime} rules to apply to the device. Default [{@link WorkTimeStandardHours}].
*/
protected WorkTime workTime;
/**
* The list of {@link Group}(s) the device belongs to.
*/
protected List groups;
/**
* Geotab DevicePlan that has been purchased for this device.
*/
protected List devicePlans;
/**
* The IANA time zone Id of the device used to determine local work times. This is typically the
* "home location" of the device. Default ["America/New_York"].
*/
protected String timeZoneId;
/**
* The minimum allowable value for maxSecondsBetweenLogs. Defaults to 0.0f.
*/
protected Float minSecondsBetweenLogs;
/**
* The maximum allowed time between logs when the ignition is on in seconds. When the value is
* exceeded, data is considered to be missing. Default [200].
*/
protected Float maxSecondsBetweenLogs;
/**
* The date the device is active from. Default [MIN_DATE].
*/
protected LocalDateTime activeFrom;
/**
* The date that the device is active to. Default [MAX_DATE].
*/
protected LocalDateTime activeTo;
/**
* Free text field where any user information can be stored and referenced for this entity.
*/
protected String comment;
/**
* The device features which have been enabled whether the feature is in use (e.g. HOS, Iridium).
* The DevicePlan property cannot be added/updated via the API.
*/
protected DeviceFlags deviceFlags;
/**
* The custom features for this device and their values.
*
* CustomFeatures property will be removed completely in a future version of the SDK.
* CustomFeatures property is only used by AutoHos, for which GoDevice#autoHos property was added.
* Backward compatibility for CustomFeatures is ensured: autoHos property takes precedence over
* customFeatures when both are set, otherwise customFeatures.
*/
protected Map customFeatures;
@Builder
@SuppressWarnings("squid:S00107")
public Device(String id, String name, Long version,
DeviceType deviceType, LocalDateTime ignoreDownloadsUntil,
String serialNumber, Integer productId, Duration timeToDownload,
WorkTime workTime, List groups, List devicePlans, String timeZoneId,
Float maxSecondsBetweenLogs, LocalDateTime activeFrom, LocalDateTime activeTo, String comment,
DeviceFlags deviceFlags, Map customFeatures) {
super(id, name, version);
this.deviceType = deviceType;
this.ignoreDownloadsUntil = ignoreDownloadsUntil;
this.serialNumber = serialNumber;
this.productId = productId;
this.timeToDownload = timeToDownload;
this.workTime = workTime;
this.groups = groups;
this.devicePlans = devicePlans;
this.timeZoneId = timeZoneId;
this.maxSecondsBetweenLogs = maxSecondsBetweenLogs;
this.activeFrom = activeFrom;
this.activeTo = activeTo;
this.comment = comment;
this.deviceFlags = deviceFlags;
this.customFeatures = customFeatures;
}
/**
* Maps the given productId to a {@link DeviceType}.
*
* @param productId The device product Id.
* @return The {@link DeviceType}
*/
public static DeviceType deviceTypeFromProductId(int productId) {
if (productId < 0) {
return DeviceType.NONE;
}
if (productId == 0) {
return DeviceType.OLD_GEOTAB;
}
if (productId <= 31 || productId >= 40 && productId <= 55) {
return DeviceType.GO2;
}
if (productId <= 39 || productId >= 56 && productId < GO4_PRODUCT_ID) {
return DeviceType.GO3;
}
if (productId >= GO4_PRODUCT_ID && productId < GO4_V3_PRODUCT_ID) {
return DeviceType.GO4;
}
if (productId >= GO4_V3_PRODUCT_ID && productId < GO5_PRODUCT_ID) {
return DeviceType.GO4V3;
}
if (productId >= GO5_PRODUCT_ID && productId < GO6_PRODUCT_ID) {
return DeviceType.GO5;
}
if (productId >= GO6_PRODUCT_ID && productId < GO7_PRODUCT_ID) {
return DeviceType.GO6;
}
if (productId >= GO7_PRODUCT_ID && productId < GO8_PRODUCT_ID) {
return DeviceType.GO7;
}
if (productId >= GO8_PRODUCT_ID && productId < GO9_PRODUCT_ID) {
return DeviceType.GO8;
}
if (productId >= GO9_PRODUCT_ID && productId <= GO_MAX_PRODUCT_ID) {
return DeviceType.GO9;
}
if (productId == GO_DRIVE_PRODUCT_ID) {
return DeviceType.GO_DRIVE_DEVICE;
}
if (ThirdPartyHelper.isThirdPartyVehicleDevice(productId)) {
return DeviceType.CUSTOM_VEHICLE_DEVICE;
}
if (ThirdPartyHelper.isThirdPartyDevice(productId)) {
return DeviceType.CUSTOM_DEVICE;
}
return DeviceType.NONE;
}
/**
* Returns the product id from its serial number.
*
* @param serialNumber The serial number.
* @return Internal product Id for a serial number
*/
public static int productIdFromSerialNumber(String serialNumber) {
int productId = 0;
if (serialNumber.length() >= 2) {
String prefix = serialNumber.substring(0, 2);
if (GO_PREFIX_TO_PRODUCT_ID_MAPPING.containsKey(prefix)) {
return GO_PREFIX_TO_PRODUCT_ID_MAPPING.get(prefix);
} else {
return ThirdPartyHelper.getThirdPartyProductId(serialNumber);
}
}
return productId;
}
/**
* Returns the string prefix of the given product ID.
*
* @param productId Internal product Id for a serial number
* @return A serial number prefix.
*/
public static String prefixFromDeviceType(int productId) {
DeviceType deviceType = deviceTypeFromProductId(productId);
switch (deviceType) {
case OLD_GEOTAB:
case GO2:
return GO2_PREFIX;
case GO3:
return GO3_PREFIX;
case GO4:
return GO4_PREFIX;
case GO4V3:
return GO4_V3_PREFIX;
case GO5:
return GO5_PREFIX;
case GO6:
return GO6_PREFIX;
case GO7:
return GO7_PREFIX;
case GO8:
return GO8_PREFIX;
case GO9:
return GO9_PREFIX;
case GO_DRIVE_DEVICE:
return GO_DRIVE_PREFIX;
default:
if (ThirdPartyHelper.THIRD_PARTY_DEVICE_TYPES.containsKey(productId)) {
return ThirdPartyHelper.THIRD_PARTY_DEVICE_TYPES.get(productId);
}
throw new IllegalArgumentException("Unsupported product id " + productId + " .");
}
}
/**
* Determine if a device is NoGps Device by its productId.
*
* @return true | false
*/
@JsonIgnore
public boolean isNoGpsDevice() {
return productId == GO8_PRODUCT_ID_NO_GPS || productId == GO9_PRODUCT_ID_NO_GPS;
}
/**
* Gets the device class that matches a {@link DeviceType}.
*
* @param deviceType {@link DeviceType}
* @return The device class
*/
public static Class extends Device> fromDeviceType(DeviceType deviceType) {
if (deviceType == null) {
return null;
}
switch (deviceType) {
case OLD_GEOTAB:
case GO2:
case GO3:
case GO4:
return GoLegacy.class;
case GO4V3:
return Go4v3.class;
case GO5:
return Go5.class;
case GO6:
return Go6.class;
case GO7:
return Go7.class;
case GO8:
return Go8.class;
case GO9:
return Go9.class;
case GO_DRIVE_DEVICE:
return GoDriveDevice.class;
case CUSTOM_DEVICE:
return CustomDevice.class;
case CUSTOM_VEHICLE_DEVICE:
return CustomVehicleDevice.class;
case NONE:
return UntrackedAsset.class;
default:
throw new IllegalArgumentException("Device Type " + deviceType + " is not supported.");
}
}
/**
* Returns the correct type of device for the given serial number.
*
* @param serialNumber The serial number for the device. For example, GV-001-000-0000.
* @return An instance of {@link Device} object that corresponds to the serial number provided
*/
public static Device fromSerialNumber(String serialNumber) throws Exception {
int productId = productIdFromSerialNumber(serialNumber);
Device device = fromProductId(productId);
device.serialNumber = serialNumber;
return device;
}
/**
* Set default values for the {@link Device} instance.
*/
public void populateDefaults() {
this.comment = Optional.ofNullable(this.comment).orElse("");
this.timeToDownload = Optional.ofNullable(this.timeToDownload)
.orElse(Duration.ofMillis(86400000L));
this.ignoreDownloadsUntil = Optional.ofNullable(this.ignoreDownloadsUntil)
.orElse(MIN_DATE);
this.timeZoneId = Optional.ofNullable(this.timeZoneId).orElse("America/New_York");
this.groups = Optional.ofNullable(this.groups)
.orElse(Lists.newArrayList(new CompanyGroup()));
this.maxSecondsBetweenLogs = Optional.ofNullable(this.maxSecondsBetweenLogs)
.orElse(DEFAULT_MAX_NO_LOG_SECONDS);
this.workTime = Optional.ofNullable(this.workTime).orElse(new WorkTimeStandardHours());
this.activeTo = MAX_DATE;
this.activeFrom = Optional.ofNullable(this.activeFrom)
.orElse(LocalDateTime.now(ZoneOffset.UTC));
}
/**
* Get the correct Device object from the given product Id.
*
* @param productId Product id.
* @return Device instance
*/
public static Device fromProductId(int productId) throws Exception {
DeviceType deviceType = deviceTypeFromProductId(productId);
Device device = (Device) Class.forName(fromDeviceType(deviceType).getName())
.getDeclaredConstructor()
.newInstance();
device.productId = productId;
return device;
}
}