ThingsBoard流程梳理

权限流程

ThingsBoard的权限管理基于Spring Security,使用其过滤器处理
从配置入手

//org.thingsboard.server.config.ThingsboardSecurityConfiguration

@Override
protected void configure(HttpSecurity http) throws Exception {
	http.headers().cacheControl().and().frameOptions().disable()
			.and()
			.cors()
			.and()
			.csrf().disable()
			.exceptionHandling()
			.and()
			.sessionManagement()
			.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
			.and()
			.authorizeRequests()
			.antMatchers(WEBJARS_ENTRY_POINT).permitAll() // Webjars
			.antMatchers(DEVICE_API_ENTRY_POINT).permitAll() // Device HTTP Transport API
			.antMatchers(FORM_BASED_LOGIN_ENTRY_POINT).permitAll() // Login end-point
			.antMatchers(PUBLIC_LOGIN_ENTRY_POINT).permitAll() // Public login end-point
			.antMatchers(TOKEN_REFRESH_ENTRY_POINT).permitAll() // Token refresh end-point
			.antMatchers(NON_TOKEN_BASED_AUTH_ENTRY_POINTS).permitAll() // static resources, user activation and password reset end-points
			.and().authorizeRequests()
			.antMatchers(WS_TOKEN_BASED_AUTH_ENTRY_POINT).authenticated() // Protected WebSocket API End-points
			.antMatchers(TOKEN_BASED_AUTH_ENTRY_POINT).authenticated() // Protected API End-points
			.and().exceptionHandling().accessDeniedHandler(restAccessDeniedHandler)
			.and()
			.addFilterBefore(buildRestLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
			.addFilterBefore(buildRestPublicLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
			.addFilterBefore(buildJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
			.addFilterBefore(buildRefreshTokenProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
			.addFilterBefore(buildWsJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
			.addFilterAfter(rateLimitProcessingFilter, UsernamePasswordAuthenticationFilter.class);
	if (oauth2Configuration != null) {
		http.oauth2Login()
				.authorizationEndpoint()
				.authorizationRequestRepository(httpCookieOAuth2AuthorizationRequestRepository)
				.authorizationRequestResolver(oAuth2AuthorizationRequestResolver)
				.and().loginPage("/oauth2Login")
				.loginProcessingUrl(oauth2Configuration.getLoginProcessingUrl())
				.successHandler(oauth2AuthenticationSuccessHandler)
				.failureHandler(oauth2AuthenticationFailureHandler);
	}
}

增加了六种过滤器

  • addFilterBefore(buildRestLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
    登录处理器
  • addFilterBefore(buildRestPublicLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
    公开的登录处理器
  • addFilterBefore(buildJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
    JWT令牌权限处理器
  • addFilterBefore(buildRefreshTokenProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
    刷新令牌处理器
  • addFilterBefore(buildWsJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
    WebSocketJWT令牌处理器
  • addFilterAfter(rateLimitProcessingFilter, UsernamePasswordAuthenticationFilter.class)
    限流处理器

接着我们重点看 登录处理器JWT令牌处理器

登录处理器

//org.thingsboard.server.config.ThingsboardSecurityConfiguration

public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login";

@Autowired
@Qualifier("defaultAuthenticationSuccessHandler")
private AuthenticationSuccessHandler successHandler;

@Autowired
@Qualifier("defaultAuthenticationFailureHandler")
private AuthenticationFailureHandler failureHandler;

@Autowired private ObjectMapper objectMapper;

@Bean
protected RestLoginProcessingFilter buildRestLoginProcessingFilter() throws Exception {
	RestLoginProcessingFilter filter = new RestLoginProcessingFilter(FORM_BASED_LOGIN_ENTRY_POINT, successHandler, failureHandler, objectMapper);
	filter.setAuthenticationManager(this.authenticationManager);
	return filter;
}

successHandler为成功处理器,创建JWT令牌写并响应
failureHandler为失败处理器,构建失败信息并响应
RestLoginProcessingFilter继承自org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter,接下来看其处理方法public abstract Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException;

//org.thingsboard.server.service.security.auth.rest.RestLoginProcessingFilter

@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
		throws AuthenticationException, IOException, ServletException {
	if (!HttpMethod.POST.name().equals(request.getMethod())) {
		if(log.isDebugEnabled()) {
			log.debug("Authentication method not supported. Request method: " + request.getMethod());
		}
		throw new AuthMethodNotSupportedException("Authentication method not supported");
	}

	LoginRequest loginRequest;
	try {
		//获取请求数据并反序列化
		loginRequest = objectMapper.readValue(request.getReader(), LoginRequest.class);
	} catch (Exception e) {
		throw new AuthenticationServiceException("Invalid login request payload");
	}

	if (StringUtils.isBlank(loginRequest.getUsername()) || StringUtils.isEmpty(loginRequest.getPassword())) {
		throw new AuthenticationServiceException("Username or Password not provided");
	}

	UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, loginRequest.getUsername());

	//创建鉴权令牌
	UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(principal, loginRequest.getPassword());
	//设置细节(客户端地址,用户代理等)
	token.setDetails(authenticationDetailsSource.buildDetails(request));
	//验证并返回
	return this.getAuthenticationManager().authenticate(token);
}

认证管理使用的是org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter.AuthenticationManagerDelegator
AuthenticationManagerDelegator是一个代理类,代理目标为org.springframework.security.authentication.ProviderManager
ProviderManagerAuthentication authenticate(Authentication authentication) throws AuthenticationException方法将遍历管理的AuthenticationProvider,根据其boolean supports(Class<?> authentication)方法判断是否支持当前认证,调用其Authentication authenticate(Authentication authentication) throws AuthenticationException方法进行认证
综上,我们找到org.thingsboard.server.service.security.auth.rest.RestAuthenticationProvider

//org.thingsboard.server.service.security.auth.rest.RestAuthenticationProvider

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
	Assert.notNull(authentication, "No authentication data provided");

	Object principal = authentication.getPrincipal();
	if (!(principal instanceof UserPrincipal)) {
		throw new BadCredentialsException("Authentication Failed. Bad user principal.");
	}

	UserPrincipal userPrincipal =  (UserPrincipal) principal;
	//判断类型
	if (userPrincipal.getType() == UserPrincipal.Type.USER_NAME) {
		String username = userPrincipal.getValue();
		String password = (String) authentication.getCredentials();
		//根据用户名和密码认证
		return authenticateByUsernameAndPassword(authentication, userPrincipal, username, password);
	} else {
		String publicId = userPrincipal.getValue();
		//根据公开Id认证
		return authenticateByPublicId(userPrincipal, publicId);
	}
}
//org.thingsboard.server.service.security.auth.rest.RestAuthenticationProvider

private Authentication authenticateByUsernameAndPassword(Authentication authentication, UserPrincipal userPrincipal, String username, String password) {
	//查询用户
	User user = userService.findUserByEmail(TenantId.SYS_TENANT_ID, username);
	if (user == null) {
		throw new UsernameNotFoundException("User not found: " + username);
	}

	try {

		//查询用户凭证
		UserCredentials userCredentials = userService.findUserCredentialsByUserId(TenantId.SYS_TENANT_ID, user.getId());
		if (userCredentials == null) {
			throw new UsernameNotFoundException("User credentials not found");
		}

		try {
			//验证用户凭证(密码是否正确、状态是否禁用、密码是否过期)
			systemSecurityService.validateUserCredentials(user.getTenantId(), userCredentials, username, password);
		} catch (LockedException e) {
			logLoginAction(user, authentication, ActionType.LOCKOUT, null);
			throw e;
		}

		if (user.getAuthority() == null)
			throw new InsufficientAuthenticationException("User has no authority assigned");

		//创建用户
		SecurityUser securityUser = new SecurityUser(user, userCredentials.isEnabled(), userPrincipal);
		logLoginAction(user, authentication, ActionType.LOGIN, null);
		//创建认证令牌并返回
		return new UsernamePasswordAuthenticationToken(securityUser, null, securityUser.getAuthorities());
	} catch (Exception e) {
		logLoginAction(user, authentication, ActionType.LOGIN, e);
		throw e;
	}
}

最后,调用RestLoginProcessingFilter的成功方法

//org.thingsboard.server.service.security.auth.rest.RestLoginProcessingFilter

@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
										Authentication authResult) throws IOException, ServletException {
	successHandler.onAuthenticationSuccess(request, response, authResult);
}

调用成功处理器根据用户信息和权限创建令牌并返回

//org.thingsboard.server.service.security.auth.rest.RestAwareAuthenticationSuccessHandler

@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
									Authentication authentication) throws IOException, ServletException {
	//获取用户
	SecurityUser securityUser = (SecurityUser) authentication.getPrincipal();

	//创建令牌
	JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser);
	//刷新令牌
	JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser);

	//令牌集合
	Map<String, String> tokenMap = new HashMap<String, String>();
	tokenMap.put("token", accessToken.getToken());
	tokenMap.put("refreshToken", refreshToken.getToken());

	response.setStatus(HttpStatus.OK.value());
	response.setContentType(MediaType.APPLICATION_JSON_VALUE);
	//写入响应
	mapper.writeValue(response.getWriter(), tokenMap);

	//清除认真属性
	clearAuthenticationAttributes(request);
}

关键流程

在这里插入图片描述

JWT令牌处理器

//org.thingsboard.server.config.ThingsboardSecurityConfiguration

public static final String WEBJARS_ENTRY_POINT = "/webjars/**";

public static final String DEVICE_API_ENTRY_POINT = "/api/v1/**";

public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login";

public static final String PUBLIC_LOGIN_ENTRY_POINT = "/api/auth/login/public";

public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token";

protected static final String[] NON_TOKEN_BASED_AUTH_ENTRY_POINTS = new String[] {"/index.html", "/assets/**", "/static/**", "/api/noauth/**", "/webjars/**",  "/api/license/**"};

public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**";

public static final String WS_TOKEN_BASED_AUTH_ENTRY_POINT = "/api/ws/**";

@Autowired
@Qualifier("jwtHeaderTokenExtractor")
private TokenExtractor jwtHeaderTokenExtractor;

protected JwtTokenAuthenticationProcessingFilter buildJwtTokenAuthenticationProcessingFilter() throws Exception {
	//跳过的路径列表
	List<String> pathsToSkip = new ArrayList<>(Arrays.asList(NON_TOKEN_BASED_AUTH_ENTRY_POINTS));
	pathsToSkip.addAll(Arrays.asList(WS_TOKEN_BASED_AUTH_ENTRY_POINT, TOKEN_REFRESH_ENTRY_POINT, FORM_BASED_LOGIN_ENTRY_POINT,
			PUBLIC_LOGIN_ENTRY_POINT, DEVICE_API_ENTRY_POINT, WEBJARS_ENTRY_POINT));
	//创建包含跳过路径的匹配器
	SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT);
	//创建过滤器
	JwtTokenAuthenticationProcessingFilter filter
			= new JwtTokenAuthenticationProcessingFilter(failureHandler, jwtHeaderTokenExtractor, matcher);
	filter.setAuthenticationManager(this.authenticationManager);
	return filter;
}

jwtHeaderTokenExtractor 为头部JWT令牌提取器
接着看JwtTokenAuthenticationProcessingFilter的处理方法

//org.thingsboard.server.service.security.auth.jwt.JwtTokenAuthenticationProcessingFilter

@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
		throws AuthenticationException, IOException, ServletException {
	//从请求头提取JWT令牌
	RawAccessJwtToken token = new RawAccessJwtToken(tokenExtractor.extract(request));
	//认证并返回
	return getAuthenticationManager().authenticate(new JwtAuthenticationToken(token));
}

和前面一样,根据令牌类型org.thingsboard.server.service.security.auth.JwtAuthenticationToken找到org.thingsboard.server.service.security.auth.jwt.JwtAuthenticationProvider


package org.thingsboard.server.service.security.auth.jwt;

import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;
import org.thingsboard.server.service.security.auth.TokenOutdatingService;
import org.thingsboard.server.service.security.auth.JwtAuthenticationToken;
import org.thingsboard.server.service.security.exception.JwtExpiredTokenException;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
import org.thingsboard.server.service.security.model.token.RawAccessJwtToken;

@Component
@RequiredArgsConstructor
public class JwtAuthenticationProvider implements AuthenticationProvider {

	private final JwtTokenFactory tokenFactory;
	private final TokenOutdatingService tokenOutdatingService;

	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		RawAccessJwtToken rawAccessToken = (RawAccessJwtToken) authentication.getCredentials();
		//解析用户
		SecurityUser securityUser = tokenFactory.parseAccessJwtToken(rawAccessToken);

		//判断过期
		if (tokenOutdatingService.isOutdated(rawAccessToken, securityUser.getId())) {
			throw new JwtExpiredTokenException("Token is outdated");
		}

		//创建并返回认证令牌
		return new JwtAuthenticationToken(securityUser);
	}

	@Override
	public boolean supports(Class<?> authentication) {
		return (JwtAuthenticationToken.class.isAssignableFrom(authentication));
	}
}

接着,调用JwtTokenAuthenticationProcessingFilter的成功方法

//org.thingsboard.server.service.security.auth.jwt.JwtTokenAuthenticationProcessingFilter

@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
										Authentication authResult) throws IOException, ServletException {
	//创建空白上下文
	SecurityContext context = SecurityContextHolder.createEmptyContext();
	//设置认证对象,即前面的JwtAuthenticationToken
	context.setAuthentication(authResult);
	//设置上下文
	SecurityContextHolder.setContext(context);
	//继续过滤链处理
	chain.doFilter(request, response);
}

至此,用户信息被写入上下文中,后续使用org.springframework.security.access.prepost.PreAuthorize注解的方法将判断上下文中的用户权限列表是否包含该权限名,实现权限的控制

关键流程

在这里插入图片描述

处理流程

大致分为基本处理设备消息处理

基本处理

通过Actor模型分发处理,之前我们已经了解了ThingsBoardActor的基本实现,今天来看一下具体的使用
通过查找org.thingsboard.server.actors.DefaultTbActorSystem的使用,找到切入点org.thingsboard.server.actors.service.DefaultActorService
重点关注其初始化方法

//org.thingsboard.server.actors.service.DefaultActorService

@Autowired
private ActorSystemContext actorContext;

@PostConstruct
public void initActorSystem() {
	log.info("Initializing actor system.");
	//设置 Actor 服务
	actorContext.setActorService(this);
	//创建 Actor 系统设置
	TbActorSystemSettings settings = new TbActorSystemSettings(actorThroughput, schedulerPoolSize, maxActorInitAttempts);
	//创建 Actor 系统
	system = new DefaultTbActorSystem(settings);

	//创建应用调度器
	system.createDispatcher(APP_DISPATCHER_NAME, initDispatcherExecutor(APP_DISPATCHER_NAME, appDispatcherSize));
	//创建租户调度器
	system.createDispatcher(TENANT_DISPATCHER_NAME, initDispatcherExecutor(TENANT_DISPATCHER_NAME, tenantDispatcherSize));
	//创建设备调度器
	system.createDispatcher(DEVICE_DISPATCHER_NAME, initDispatcherExecutor(DEVICE_DISPATCHER_NAME, deviceDispatcherSize));
	//创建规则调度器
	system.createDispatcher(RULE_DISPATCHER_NAME, initDispatcherExecutor(RULE_DISPATCHER_NAME, ruleDispatcherSize));

	//设置 Actor 系统
	actorContext.setActorSystem(system);

	//创建应用 Actor
	appActor = system.createRootActor(APP_DISPATCHER_NAME, new AppActor.ActorCreator(actorContext));
	//设置应用 Actor
	actorContext.setAppActor(appActor);

	//创建统计 Actor
	TbActorRef statsActor = system.createRootActor(TENANT_DISPATCHER_NAME, new StatsActor.ActorCreator(actorContext, "StatsActor"));
	//设置统计 Actor
	actorContext.setStatsActor(statsActor);

	log.info("Actor system initialized.");
}

接着看ActorSystemContext,代码比较多,重点找消息传递相关的方法

//org.thingsboard.server.actors.ActorSystemContext

public void tell(TbActorMsg tbActorMsg) {
	appActor.tell(tbActorMsg);
}

public void tellWithHighPriority(TbActorMsg tbActorMsg) {
	appActor.tellWithHighPriority(tbActorMsg);
}

可见,消息统一交给了appActor处理
查看AppActordoProcess方法

//org.thingsboard.server.actors.app.AppActor

@Override
protected boolean doProcess(TbActorMsg msg) {
	if (!ruleChainsInitialized) {
		//初始化租户 Actor
		initTenantActors();
		ruleChainsInitialized = true;
		if (msg.getMsgType() != MsgType.APP_INIT_MSG && msg.getMsgType() != MsgType.PARTITION_CHANGE_MSG) {
			log.warn("Rule Chains initialized by unexpected message: {}", msg);
		}
	}
	switch (msg.getMsgType()) {
		case APP_INIT_MSG:
			break;
		case PARTITION_CHANGE_MSG:
			ctx.broadcastToChildren(msg);
			break;
		case COMPONENT_LIFE_CYCLE_MSG:
			onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
			break;
		case QUEUE_TO_RULE_ENGINE_MSG:
			onQueueToRuleEngineMsg((QueueToRuleEngineMsg) msg);
			break;
		case TRANSPORT_TO_DEVICE_ACTOR_MSG:
			onToDeviceActorMsg((TenantAwareMsg) msg, false);
			break;
		case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
		case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
		case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
		case DEVICE_EDGE_UPDATE_TO_DEVICE_ACTOR_MSG:
		case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
		case DEVICE_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
		case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
		case REMOVE_RPC_TO_DEVICE_ACTOR_MSG:
			onToDeviceActorMsg((TenantAwareMsg) msg, true);
			break;
		case EDGE_EVENT_UPDATE_TO_EDGE_SESSION_MSG:
			onToTenantActorMsg((EdgeEventUpdateMsg) msg);
			break;
		case SESSION_TIMEOUT_MSG:
			ctx.broadcastToChildrenByType(msg, EntityType.TENANT);
			break;
		default:
			return false;
	}
	return true;
}

首次会进行租户Actor初始化

//org.thingsboard.server.actors.app.AppActor

private void initTenantActors() {
	log.info("Starting main system actor.");
	try {
		// This Service may be started for specific tenant only.
		//独立的租户标识
		Optional<TenantId> isolatedTenantId = systemContext.getServiceInfoProvider().getIsolatedTenant();
		if (isolatedTenantId.isPresent()) {
			//查询租户
			Tenant tenant = systemContext.getTenantService().findTenantById(isolatedTenantId.get());
			if (tenant != null) {
				log.debug("[{}] Creating tenant actor", tenant.getId());
				//获取或创建租户 Actor
				getOrCreateTenantActor(tenant.getId());
				log.debug("Tenant actor created.");
			} else {
				log.error("[{}] Tenant with such ID does not exist", isolatedTenantId.get());
			}
		} else if (systemContext.isTenantComponentsInitEnabled()) {
			//统一的租户服务且开启初始化
			PageDataIterable<Tenant> tenantIterator = new PageDataIterable<>(tenantService::findTenants, ENTITY_PACK_LIMIT);
			//当前是否规则引擎服务
			boolean isRuleEngine = systemContext.getServiceInfoProvider().isService(ServiceType.TB_RULE_ENGINE);
			//当前是否核心服务
			boolean isCore = systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE);
			//遍历租户
			for (Tenant tenant : tenantIterator) {
				//获取租户配置
				TenantProfile tenantProfile = tenantProfileCache.get(tenant.getTenantProfileId());
				//判断是否为核心服务或是否为规则引擎服务且租户未使用独立的规则引擎
				if (isCore || (isRuleEngine && !tenantProfile.isIsolatedTbRuleEngine())) {
					log.debug("[{}] Creating tenant actor", tenant.getId());
					//获取或创建租户 Actor
					getOrCreateTenantActor(tenant.getId());
					log.debug("[{}] Tenant actor created.", tenant.getId());
				}
			}
		}
		log.info("Main system actor started.");
	} catch (Exception e) {
		log.warn("Unknown failure", e);
	}
}

根据配置使用getOrCreateTenantActor方法预先创建租户Actor,将接收到的消息交由租户Actor处理
租户Actor根据消息的类型,再交由设备Actor或规则链Actor处理

关键流程

在这里插入图片描述

设备消息处理

设备消息处理主要根据设备发送的消息进行对应的处理,逻辑比较多,这里画一个简略的流程
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Thingsboard 下发指令的流程如下: 1. 连接和鉴权:首先,设备需要与 Thingsboard 云平台建立连接,并通过鉴权验证设备的身份和权限。设备会发送认证请求到云平台,云平台会返回一个访问令牌,用于后续通信的身份验证。 2. 创建与设备的关联:在连接鉴权成功后,云平台会为设备创建一个设备实例,并将其添加到设备管理界面。这样,云平台就能够追踪和管理设备的状态和属性。 3. 生成指令:在设备实例被创建后,用户可以通过设备管理界面生成指令。指令可能包含设备的配置参数、操作命令或其他要求,用户可以自定义指令内容和格式。 4. 下发指令:生成指令后,用户可以将指令下发给设备。云平台会将指令发送给设备的唯一标识符,设备根据标识符找到对应的指令信息。 5. 设备接收指令:设备通过连接和监听自己的消息通道,接收到来自云平台的指令。设备会解析指令并根据指令要求执行相应的操作。 6. 反馈执行结果:设备执行完指令后,会向云平台返回执行结果。云平台可以将设备的执行结果展示给用户,以便用户了解指令是否被成功执行。 7. 更新设备状态:设备执行完指令后,其状态可能会发生变化。云平台会根据设备的反馈结果更新设备的状态信息,并将最新的设备状态显示在设备管理界面上。 总之,Thingsboard 的指令下发流程是通过连接与鉴权、创建与设备的关联、生成指令、下发指令、设备接收指令、反馈执行结果和更新设备状态等步骤完成的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值