Last time device moved

Davea month ago

Thanks @dsegura I meant when the page rerenders such as going onto the reports page and running a report, when returning to the Device row page the stopTime shows a month again until it’s recalculate its actual stop time. I’m trying to implement useState function to preserve the stop time until it’s updated with a new stop time and not reset to default (a month)
It seems to still show a stop time when in motion also.
Maybe a more efficient way of implementing this would be to use a counter when motion has stopped and reset when motion starts? This would save the calculation of time of x

dseguraa month ago

I'm sorry, I don't understanding this. It does not happen to me that it defaults back to a month until it is recalculated, at least I don't think it does. Could you provide your code/video or images of this issue happening?

And if you try that more efficient implementation, please do share it if it works. Rigt now I'm working o other things so I can't test the changes you've been describing, but I plan to eventually of course

Davea month ago

No problem, il get the code later. Here’s a screenshot of when it has the stopped times calculated and then one from when after Iv left the screen and gone into a report

Davea month ago

IMG_6295.jpeg

dseguraa month ago

Only thing that occurs to me without the code is that you have setted a month somewhere, and instructed your function to always check that data. Just an assumption of course, I'd need the code. But yeah this definetely does not happen to me. Mayeb a missing useEffect or State for devicedata.

Do you only have this problem with the StopTime or also with other position data like speed or similar?

Davea month ago

There was meant to be another screen shot to show when they reset to a month but it didn't upload. I was thinking the same thing about a missing useEffect or useState to hold the previous values until updated as I don't have either of these in the code, thank you for your help its much appreciated. here's the relevant code...

Davea month ago
const getTimeColor = (stopTime) => {
    const minutesSinceUpdate = dayjs().diff(dayjs(stopTime), 'minutes', useState(item.stopTime));



    if (minutesSinceUpdate <= 30) {
      return classes.success;
    } if (minutesSinceUpdate > 30 && minutesSinceUpdate <= 60) {
      return classes.warning;
    } if (minutesSinceUpdate > 60 && minutesSinceUpdate <= 24 * 60) {
      return classes.error;
    }
  };

  const secondaryText = () => {
    let moving = 0;
    if (item.motionStreak && item.status === 'online') {
      moving = formatStatus(item.status, t);
      return (
        <>
          {deviceSecondary && item[deviceSecondary] && `${item[deviceSecondary]} • `}
          <span className={classes.green}>{moving}</span>
        </>
      )
    } else if(!item.lastUpdate) {
      moving = formatStatus(item.status, t);
      return (
        <>
          {deviceSecondary && item[deviceSecondary] && `${item[deviceSecondary]} • `}
          <span className={getTimeColor(item.stopTime)}>{moving}</span>
        </>
      )
    } else {
      moving = dayjs(item.stopTime).fromNow(true);
        return (
          <>
            {deviceSecondary && item[deviceSecondary] && `${item[deviceSecondary]} • `}
            <Typography position="relative" sx={{ top: 'auto', bottom: 'auto', fontSize: 'auto' , }} variant="caption">Stationary for </Typography>
            <span className={getTimeColor(item.stopTime)}>{moving}</span>
          </>
        );
    }
  };
  
  return (
    <div style={style}>
      <ListItemButton
        key={item.id}
        onClick={() => dispatch(devicesActions.selectId(item.id))}
        disabled={!admin && item.disabled}
      >
        <ListItemAvatar>
          <Avatar style={{ background: getStatusColorIcon(item) }}>
            <img className={classes.icon} src={mapIcons[mapIconKey(item.category)]} alt="" />
          </Avatar>
        </ListItemAvatar>
        <ListItemText
          primary={item[devicePrimary]}
          primaryTypographyProps={{
            noWrap: false,
          }}
          secondary={secondaryText()}
          secondaryTypographyProps={{
            noWrap: true,
            className: classes.clickable
          }}
          onClick={(e) => {
            e.stopPropagation();
            dispatch(devicesActions.selectId(item.id));
          }}
        />
Davea month ago

Forgot to mention the useState that is in the code isn’t working.

dseguraa month ago

Good morning!

From this code I only notice differences between mine in the getTimeColor function. I don't use a useState, and you are not using the if's as I do. Yours are too conditional and don't account for outliers, there may be some problems for you there if some devices get past those hours.

It's no wonder that the useState does nothing there, it doesn't seem to be placed well. I recommend re-reading useState documentation, I'm not a react focused developer, I'm still learning from and for Traccar, but for my understanding, it is used to store data to be read in the rest of the component. That's not what you want here, since it is a changing data.

For the stopTime to be recalculated whilst in the deviceRow page,you need a useState but for differents part of the component, such as positions:

  const positions = useSelector((state) => state.session.positions[item.id]); // almacena la posición actual del dispositivo desde el estado de la aplicación.

Here I leave my code but as I said it oes not differ much from yours in the use of getTimeColor.

  // Función para definir el color dependiendo del estado y de la última conexión
  const getTimeColor = (stopTime) => {
    const minutesSinceUpdate = dayjs().diff(dayjs(stopTime), 'minutes');

    if (minutesSinceUpdate <= 5) {
      return classes.green; // Si han pasado menos de 5 minutos: Verde
    } else if (minutesSinceUpdate > 5 && minutesSinceUpdate <= 60) {
      return classes.yellow; // Si han pasado más de 5 minutos: Amarillo
    } else if (minutesSinceUpdate > 60 && minutesSinceUpdate <= 24 * 60) {
      return classes.red; // Si han pasado más de 1 hora: Rojo
    } else {
      return classes.grey; // Si han pasado más de 1 día: Gris
    }
  };


  // Función para mostrar al lado del nombre del dispositivo si está online o hace cuanto que está offline
  const secondaryText = () => {
    let moving = 0;
    if (item.motionStreak && item.status === 'online') {
      moving = formatStatus(item.status, t);
      return (
        <>
          {deviceSecondary && item[deviceSecondary] && `${item[deviceSecondary]} • `}
          <span className={classes.green}>{moving}</span>
        </>
      )
    } else if (!item.lastUpdate) {
      moving = formatStatus(item.status, t);
      return (
        <>
          {deviceSecondary && item[deviceSecondary] && `${item[deviceSecondary]} • `}
          <span className={getTimeColor(item.stopTime)}>{moving}</span>
        </>
      )
    } else {
      moving = dayjs(item.stopTime).fromNow(true);
      return (
        <>
          {deviceSecondary && item[deviceSecondary] && `${item[deviceSecondary]} • `}
          <span className={getTimeColor(item.stopTime)}>{moving}</span>
        </>
      );
    }
  };
Dave24 days ago

Thanks @dsegura,
I managed to use a stopwatch like timer to show device stop time, unfortunately I had the same results (the timer resetting on re-renders)
so looking back at the java code in DeviceResource.java was it correct to add the getExtended code or was it meant to be implemented in the original get code? Maybe this is my issue?

@Path("extended")
    @GET
    public Collection<Device> getExtended(
            @QueryParam("all") boolean all,
            @QueryParam("userId") long userId,
            @QueryParam("uniqueId") List<String> uniqueIds,
            @QueryParam("id") List<Long> deviceIds) throws StorageException {

        Collection<Device> devices = get(all, userId, uniqueIds, deviceIds);
        for (Device device : devices) {
            DeviceUtil.deviceToExtendedDevice(storage, device);
        }

        return devices;
    }

    @GET
    public Collection<Device> get(
            @QueryParam("all") boolean all, @QueryParam("userId") long userId,
            @QueryParam("uniqueId") List<String> uniqueIds,
            @QueryParam("id") List<Long> deviceIds) throws StorageException {
dsegura22 days ago

I'd say the problem is more likely to be on Connection manager, since the device updates late/after clicking away. If it's of any help, here my DeviceUtil.java, with all regarding the extended and previous gets:

  @GET
    public Collection<Device> get(
            @QueryParam("all") boolean all, @QueryParam("userId") long userId,
            @QueryParam("uniqueId") List<String> uniqueIds,
            @QueryParam("id") List<Long> deviceIds) throws StorageException {

        if (!uniqueIds.isEmpty() || !deviceIds.isEmpty()) {

            List<Device> result = new LinkedList<>();
            for (String uniqueId : uniqueIds) {
                result.addAll(storage.getObjects(Device.class, new Request(
                        new Columns.All(),
                        new Condition.And(
                                new Condition.Equals("uniqueId", uniqueId),
                                new Condition.Permission(User.class, getUserId(), Device.class)))));
            }
            for (Long deviceId : deviceIds) {
                result.addAll(storage.getObjects(Device.class, new Request(
                        new Columns.All(),
                        new Condition.And(
                                new Condition.Equals("id", deviceId),
                                new Condition.Permission(User.class, getUserId(), Device.class)))));
            }
            return result;

        } else {

            var conditions = new LinkedList<Condition>();

            if (all) {
                if (permissionsService.notAdmin(getUserId())) {
                    conditions.add(new Condition.Permission(User.class, getUserId(), baseClass));
                }
            } else {
                if (userId == 0) {
                    conditions.add(new Condition.Permission(User.class, getUserId(), baseClass));
                } else {
                    permissionsService.checkUser(getUserId(), userId);
                    conditions.add(new Condition.Permission(User.class, userId, baseClass).excludeGroups());
                }
            }

            return storage.getObjects(baseClass, new Request(new Columns.All(), Condition.merge(conditions)));

        }
    }

    @Path("extended")
    @GET
    public Collection<Device> getExtended(
            @QueryParam("all") boolean all,
            @QueryParam("userId") long userId,
            @QueryParam("uniqueId") List<String> uniqueIds,
            @QueryParam("id") List<Long> deviceIds) throws StorageException {

        Collection<Device> devices = get(all, userId, uniqueIds, deviceIds);
        for (Device device : devices) {
            DeviceUtil.deviceToExtendedDevice(storage, device);
        }

        return devices;
    }

My Connection Manager is almost 400 lines, so i don't know how to select the parts that would be useful to you. The use hasn't changed since I uploaded it to the first solution on this post