WebSocket - odpowiedz zwrotna

0

Jest w trakcie pisania aplikacji IoT za pomocą której, można sterować urządzeniami (Arduino, esp8266 itd.), bądź odczytywać różne pomiary.
Backend to: Spring boot, REST, JPA, WebSocket
Frontend to: JSP + JQery (Ajax).

Aktualny stan:
Mikrokontrolery łączą się z serwerem używając websocketów.
Do każdego urządzenia przypisany jest indywidualni użytkownik.
Następnie rejestrują się jeśli nie ma ich w bazie i co 500ms wysyłają wiadomość.
Poniżej przykładowa wiadomość microkontrollera do backendu:

{  
   "id":"OUTDOOR1",
   "label":"Arduino mini pro",
   "type":"INDOOR",
   "lastSeen":"2019-04-27T22:07:11.566Z",
   "internalIP":null,
   "modules":[  
      {  
         "type":"switch",
         "id":4,
         "internalId":0,
         "label":"Pokój Marcina: światło",
         "isOn":false
      },
      {  
         "type":"switch",
         "id":5,
         "internalId":1,
         "label":"Pokój Marcina: lampka",
         "isOn":true
      },
      {  
         "type":"temperatureSensor",
         "id":6,
         "internalId":1,
         "label":"Termometr_1",
         "temperature":34.22
      },
      {  
         "type":"humiditySensor",
         "id":7,
         "internalId":2,
         "label":"Wilgotoność pow.",
         "humidity":55.0
      },
      {  
         "type":"switchDim",
         "id":8,
         "internalId":1,
         "label":"Pokój Marcina: lampka",
         "isOn":true,
         "dim":50
      }
   ]
}

Rejestracja i aktualizacja odbywa się w następujący sposób:(otrzymana wiadomość od mikrokontrolera poprzez WebSocket )

    @MessageMapping("/microcontroller")
    public void handleReading(Microcontroller inbound) {
        Optional<Microcontroller> found = microcontrollerRepository.findById(inbound.getId());

        if (!found.isPresent()) {
            inbound.getModules().forEach(m -> moduleRepository.save(m));
            inbound.setLastSeen(Instant.now());
            Microcontroller saved = microcontrollerRepository.save(inbound);
            log.info("Microcontroller has been saved: " + saved.toString());
        } else {
            List<Module> modules = found.get().getModules();
            List<Module> incomingModules = inbound.getModules();
            if (incomingModules.size() > modules.size()) {
                throw new MoreModulesThanExpectedException(incomingModules.size(), modules.size());
            }
            modules.forEach(m -> moduleRepository.save(m.update(incomingModules.stream()
                            .filter(i -> i.getInternalId().equals(m.getInternalId())).findFirst().get()
                    )
            ));
            found.get().setLastSeen(Instant.now());
            Microcontroller saved = microcontrollerRepository.save(found.get());
            log.info("Microcontroller has been updated: " + saved.toString());

        }
    }

Niżej screenshoot kawałka strony https://4programmers.net/uploads/attachment/5c/5ccecc6616055.pngploads/attachment/5c/5ccecc6616055.png)

###Aktualnie aktualizacje parametrów mikrokontrolera wygląda następująco.
Z racji tego, że WebScoket pozwala na komunikację dwustronną, backend wysyła wiadomość(np. obiekt Switch ) do mikrokontrolera z nowym stanem modułu (np. przełącznik).
Tak jak wspomniałem microkontroller wysyła co 500ms powyższy JSON do aplikacji i dopiero wtedy następuje wysłanie wiadomości zwrotnej.

###W przypadku wciśnięcia przycisku on/off:##
Wysyłam za pomocą Ajax żądanie http z wiadomością do backend'u, następnie wiadomość jest przekazywana do mikrokontrollera za pomocą websocket'ów.
Problem mam w tym miejscu.
Nie wiem, w jaki sposób wyłapać to, że wiadomość została faktycznie przekazana do mikrokontrollera.
Kod odpowiedzi http dostanę 200, ale wiadomości wysyłane są docelowo przez websocket'y.

###Proszę was o pomysły, w jaki sposób byście to rozwiązali.
Jest to projekt hobbystyczny
Fajnie, gdyby ktoś wypunktował przejścia np.
F: Frontend
M: Micocontroller
B: Backend

  1. F:Wysłanie zadania http przez ajax
  2. B:W rest kontrolerze wysłanie wiadomości przez webscoket
    .....
  3. F:Po kliknięciu w przycisk on/off, zablokować go.
  4. B: W przypadku dostania wiadomości zwrotnej zaktualizować dane w bazie
    itd.
0

Prawdę mówiąc nie widzę w ogóle potrzeby robienia tutaj WebSocketów na linii backend <-> microcontroller.

Następnie rejestrują się jeśli nie ma ich w bazie i co 500ms wysyłają wiadomość.

Skoro wiadomości wysyłane są co jakiś czas to czemu nie wysyłać tego najzwyczajniej zwykłym REST'em?
Albo jeszcze inaczej, choć nie wiem czy to nie będzie przerost formy nad treścią (zakladając, że tych danych może być sporo) - za pomocą jakieś kolejki między backendem, a microcontrollerem.

Ten websocket bardziej by się nadawał na linii front <-> backend na zasadzie: jak microcontroller poinformuje backend to backend musi jakoś poinformować frontend. Tutaj wchodzi websocket, aby front nie musiał odpytywać backend co chwilę w tle.

0

Jeśli nie będzie WbeSocketów na lini backend <-> microcontroller, to w jaki sposób wyślę do microcontrollera jakąś zmianę z backendu?
Nie chce robić serwera po stronie micokontrolera do obsługi żądań rest, ponieważ musiałbym mieć publiczne IP dla każdego z urządzeń.
Aktualnie wystarczy że każdy mikrokontroler będzie miał dostęp do dowolnej sieci.

0

to w jaki sposób wyślę do microcontrollera jakąś zmianę z backendu?

Właśnie po kolejce.

Pytanie do WebSocketów - zabezpieczasz to? Jak?

0

Nie za bardzo wiem, jakby miała taka kolejka wyglądać.
Możesz rozjaśnić?

Nie chciałbym usuwać websocket'ów na linii backend <-> microcontroller.
Zbyt dużo czasu na to poświęciłem.

Zabezpieczam tak:

@Order(Ordered.HIGHEST_PRECEDENCE + 99)
public class WebSocketAuthenticationSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
    @Autowired
    private AuthChannelInterceptorAdapter authChannelInterceptorAdapter;

    @Override
    public void registerStompEndpoints(final StompEndpointRegistry registry) {
        // Endpoints are already registered on WebSocketConfig, no need to add more.
    }
    @Override
    protected void customizeClientInboundChannel(ChannelRegistration registration) {
        registration.interceptors(authChannelInterceptorAdapter);
    }

    @Override
    protected void configureInbound(MessageSecurityMetadataSourceRegistry message) {
        message
                .nullDestMatcher().permitAll()
                .simpTypeMatchers(MESSAGE, SUBSCRIBE)
                .authenticated();
    }
    @Override
    protected boolean sameOriginDisabled() {
        return true;
    }
}

Niżej adapter.

@Service
public class AuthChannelInterceptorAdapter implements ChannelInterceptor {

    private static final String USERNAME_HEADER = "login";
    private static final String PASSWORD_HEADER = "passcode";

    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private AuthenticationManager authenticationManager;

    @Override
    public Message<?> preSend(final Message<?> message, final MessageChannel channel) throws AuthenticationException {
        final StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
        if (StompCommand.CONNECT == accessor.getCommand()) {
            final String username = accessor.getFirstNativeHeader(USERNAME_HEADER);
            final String password = accessor.getFirstNativeHeader(PASSWORD_HEADER);

            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            UsernamePasswordAuthenticationToken user = new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
            accessor.setUser(user);
        }
        return message;
    }
...
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1