Prevent Errors

Error messages are the most critical interactions between the user and the rest of the system. They happen when communication between the user and the system is near breaking point.

It is easy to think of an error as being caused by a wrong input from the user. But people make mistakes in predictable, systematic ways. So it is possible to 'debug' the communication between the user and the rest of the system just as you would between other system components.

For instance, say you want the user to enter a date within an allowed range. Rather than letting the user enter any date, it is better to offer a device such as a list or calendar showing only the allowed dates. This eliminates any chance of the user entering a date outside of the range.

Formatting errors are another common problem. For instance, if a user is presented with a Date text field and enters an unambiguous date such as "July 29, 2012" it is unreasonable to reject it simply because it is not in a preferred format (such as "DD/MM/YYYY"). It is worse still to reject "29 / 07 / 2012" because it contains extra spaces — this kind of problem is particularly hard for users to understand as the date appears to be in the desired format.

This error occurs because it is easier to reject the date than parse the three or four most common date formats. These kind of petty errors lead to user frustration, which in turn lead to additional errors as the user loses concentration. Instead, respect users' preference to enter information, not data.

Another way of avoiding formatting errors is to offer cues — for instance, with a label within the field showing the desired format ("DD/MM/YYYY"). Another cue might be to divide the field into three text boxes of two, two, and four characters.

Cues are different from instructions: Cues tend to be hints; instructions are verbose. Cues occur at the point of interaction; instructions appear before the point of interaction. Cues provide context; instructions dictate use.

In general, instructions are ineffective at preventing error. Users tend to assume that interfaces will work in line with their past experience ("Surely everyone knows what 'July 29, 2012' means?"). So instructions go unread. Cues nudge users away from errors.

Another way of avoiding errors is to offer defaults. For instance, users typically enter values that correspond to today, tomorrow, my birthday, my deadline, or the date I entered last time I used this form. Depending on context, one of these is likely to be a good choice as a smart default.

Whatever the cause, systems should be tolerant of errors. You can do this by providing multiple levels of undo to all actions — and in particular actions which have the potential to destroy or amend users' data.

Logging and analyzing undo actions can also highlight where the interface is drawing users into unconscious errors, such as persistently clicking on the 'wrong' button. These errors are often caused by misleading cues or interaction sequences that you can redesign to prevent further error.

Whichever approach you take, most errors are systematic — the result of misunderstandings between the user and the software. Understanding how users think, interpret information, make decisions, and input data will help you debug the interactions between your software and your users.

by Giles Colborne

This work is licensed under a Creative Commons Attribution 3

#ifdef CONFIG_TP_PPSK static int hostapd_wpa_auth_pmk_match(struct wpa_state_machine *sm, u8 *pmk, size_t pmk_len) { struct wpa_ptk PTK; size_t mic_len = 0; u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN]; u8 pmk_r1[PMK_LEN_MAX]; size_t key_len; mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len); os_memset(&PTK, 0, sizeof(PTK)); #ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) { os_memcpy(sm->xxkey, pmk, pmk_len); sm->xxkey_len = pmk_len; } #endif /* CONFIG_IEEE80211R_AP */ if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK, 0, pmk_r0, pmk_r1, pmk_r0_name, &key_len) < 0) return 0; if (mic_len && wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK, sm->last_rx_eapol_key, sm->last_rx_eapol_key_len) == 0) { if (sm->PMK != pmk) { os_memcpy(sm->PMK, pmk, pmk_len); sm->pmk_len = pmk_len; } return 1; } return 0; } static const u8 * hostapd_wpa_auth_get_psk_in_ppsk(struct wpa_state_machine *sm, const u8 *prev_psk, const u8 *req_pmk_r1_name, int *foundFlag) { struct hostapd_data *hapd = NULL; struct sta_info *sta = NULL; struct hostapd_wpa_psk *wpa_psk = NULL; struct hostapd_wpa_psk_private *wpa_psk_pri = NULL; PPSK_PARAM_NODE *ppskParam = NULL; struct timeval tv; u64 timestamp = 0; u8 pwd[HOSTAPD_PPSK_PWD_SIZE] = {0}; u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN]; u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN]; struct wpa_authenticator *wpa_auth = sm->wpa_auth; const u8 *mdid = wpa_auth->conf.mobility_domain; const u8 *r0kh = sm->r0kh_id; size_t r0kh_len = sm->r0kh_id_len; const u8 *r1kh = wpa_auth->conf.r1_key_holder; const u8 *ssid = wpa_auth->conf.ssid; size_t ssid_len = wpa_auth->conf.ssid_len; const u8 *psk; if (NULL == sm || NULL == sm->wpa_auth || NULL == sm->wpa_auth->cb_ctx || NULL == foundFlag) { wpa_printf(MSG_ERROR, "get_psk_in_ppsk input param is NULL"); return NULL; } /* 相较于hostapd_wpa_auth_get_psk,此处省略了SAE和OWE过程,如果后续适配时存在需求,请自行修改 */ *foundFlag = 0; hapd = (struct hostapd_data *) sm->wpa_auth->cb_ctx; sta = ap_get_sta(hapd, sm->addr); if (prev_psk) { return NULL; } if (sta) { /* if psk from controller is set, return it */ if (sta->ppsk_psk_is_set) { /* In the process of FT(Fast BSS Transition), cannot reach this point. */ /* controller has returned the result. Set ppsk_psk as the PMK and return directly here. */ if (NULL == prev_psk) { memset(&tv, 0, sizeof(tv)); gettimeofday(&tv, NULL); if (sta->finishTime && sta->finishTime < tv.tv_sec) { wpa_printf(MSG_DEBUG, "The current time is not within the valid time for authentication."); return NULL; } wpa_snprintf_hex_uppercase(pwd, sizeof(pwd), sta->ppsk_psk, PMK_LEN); /* notify the pmk to driver */ // hostapd_drv_set_ppsk_pwd(hapd, sm->addr, pwd); *foundFlag = 1; return sta->ppsk_psk; } } } else { /* When reaching this function, theoretically, the STA info has already been created. To prevent errors, an add operation is performed here. */ sta = ap_sta_add(hapd, sm->addr); if (!sta) { wpa_printf(MSG_ERROR, "ap_sta_add() failed"); return NULL; } } /* firstly get the private psk, if not found, try to get group psk */ wpa_psk_pri = hostapd_get_private_wpa_psk(hapd->conf, sm->addr); if (wpa_psk_pri) { if (NULL != req_pmk_r1_name) { /* FT流程,计算出pmk r1 name进行比对 */ if (0 == wpa_derive_pmk_r0((u8 *)wpa_psk_pri->psk, PMK_LEN, ssid, ssid_len, mdid, r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name, 0) && 0 == wpa_derive_pmk_r1(pmk_r0, PMK_LEN, pmk_r0_name, r1kh, sm->addr, pmk_r1, pmk_r1_name)&& 0 == os_memcmp_const(pmk_r1_name, req_pmk_r1_name, WPA_PMK_NAME_LEN)) { ppskParam = &wpa_psk_pri->paramNode; psk = wpa_psk_pri->psk; os_memcpy(pwd, wpa_psk_pri->pwd, HOSTAPD_PPSK_PWD_SIZE); } } else { if (1 == hostapd_wpa_auth_pmk_match(sm, (u8 *)wpa_psk_pri->psk, PMK_LEN)) { ppskParam = &wpa_psk_pri->paramNode; psk = wpa_psk_pri->psk; os_memcpy(pwd, wpa_psk_pri->pwd, HOSTAPD_PPSK_PWD_SIZE); } } } else { for (wpa_psk = hapd->conf->ssid.wpa_psk; wpa_psk != NULL; wpa_psk = wpa_psk->next) { if (NULL != req_pmk_r1_name) { /* FT流程,计算出pmk r1 name进行比对 */ if (0 == wpa_derive_pmk_r0((u8 *)wpa_psk->psk, PMK_LEN, ssid, ssid_len, mdid, r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name, 0) && 0 == wpa_derive_pmk_r1(pmk_r0, PMK_LEN, pmk_r0_name, r1kh, sm->addr, pmk_r1, pmk_r1_name)&& 0 == os_memcmp_const(pmk_r1_name, req_pmk_r1_name, WPA_PMK_NAME_LEN)) { ppskParam = &wpa_psk->paramNode; psk = wpa_psk->psk; os_memcpy(pwd, wpa_psk->pwd, HOSTAPD_PPSK_PWD_SIZE); break; } } else { if (1 == hostapd_wpa_auth_pmk_match(sm, (u8 *)wpa_psk->psk, PMK_LEN)) { ppskParam = &wpa_psk->paramNode; psk = wpa_psk->psk; os_memcpy(pwd, wpa_psk->pwd, HOSTAPD_PPSK_PWD_SIZE); break; } } } } /* A notify request needs to be sent to the controller to determine whether the PMK is available: */ /* 1. The timer starts when the device is connected and ends when the end time is 0 */ /* 2. The number of PMKs that can be accessed is limited. */ if (ppskParam) { *foundFlag = 1; sta->downLimit = ppskParam->downLimit; sta->upLimit = ppskParam->upLimit; sta->finishTime = ppskParam->endTime; if (PPSK_EXPIRE_TYPE_DAILY == ppskParam->expireType) { /* calculate the daily available time. */ if (1 == hostapd_ppsk_check_time_is_in_Rule(ppskParam->startTime, ppskParam->endTime, &sta->finishTime)) { goto success; } } else { memset(&tv, 0, sizeof(tv)); gettimeofday(&tv, NULL); timestamp = (unsigned long long)tv.tv_sec; if ((ppskParam->startTime && ppskParam->startTime > tv.tv_sec) || (ppskParam->endTime && ppskParam->endTime < tv.tv_sec)) { /* 在不允许接入的时间,返回NULL */ wpa_printf(MSG_DEBUG, "PMK is unavailable now"); goto fail; } else { goto success; } } } fail: return NULL; success: #ifdef CONFIG_TP_DYVLAN /* notify the binding vlan to driver */ if (hapd->conf->ssid.ssidDyVlanEn && VLAN_IS_VALID(ppskParam->ppsk_vlan)) { hostapd_drv_set_tp_dyvlan(hapd, sm->addr, ppskParam->ppsk_vlan); sta->dyVlanID = ppskParam->ppsk_vlan; } #endif /* CONFIG_TP_DYVLAN */ /* notify the pmk to driver */ // hostapd_drv_set_ppsk_pwd(hapd, sm->addr, pwd); return psk; } #endif /* CONFIG_TP_PPSK */总结下作用
11-28
@attached class Agent(BaseAgent): def __init__(self, agent_type="player", device=None, logger=None, monitor=None): self.model = Model(device=device) self.optim = torch.optim.RMSprop(self.model.parameters(), lr=Config.LR) self._eps = Config.START_EPSILON_GREEDY self.end_eps = Config.END_EPSILON_GREEDY self.eps_decay = Config.EPSILON_DECAY self.head_dim = [ Config.DIM_OF_ACTION_PHASE_1, Config.DIM_OF_ACTION_DURATION_1, Config.DIM_OF_ACTION_PHASE_2, Config.DIM_OF_ACTION_DURATION_2, ] self.device = device self.epsilon = Config.EPSILON self.logger = logger self.monitor = monitor self.preprocess = FeatureProcess(logger) self.last_action = None self.algorithm = Algorithm(self.model, self.optim, self.device, self.logger, self.monitor) super().__init__(agent_type, device, logger, monitor) def reset(self): self.preprocess.reset() def __predict_detail(self, list_obs_data, exploit_flag=False): feature = [obs_data.feature for obs_data in list_obs_data] model = self.model model.eval() self._eps = max(self.end_eps, self._eps * self.eps_decay) if np.random.rand() >= self._eps or exploit_flag: with torch.no_grad(): res = model(feature)[0] list_phase_1 = torch.argmax(res[0], dim=1).cpu().view(-1, 1).tolist()[0] list_duration_1 = torch.argmax(res[1], dim=1).cpu().view(-1, 1).tolist()[0] list_phase_2 = torch.argmax(res[2], dim=1).cpu().view(-1, 1).tolist()[0] list_duration_2 = torch.argmax(res[3], dim=1).cpu().view(-1, 1).tolist()[0] else: random_action = np.random.choice(self.head_dim[0], len(list_obs_data)) list_phase_1 = random_action random_action = np.random.choice(self.head_dim[1], len(list_obs_data)) list_duration_1 = random_action random_action = np.random.choice(self.head_dim[2], len(list_obs_data)) list_phase_2 = random_action random_action = np.random.choice(self.head_dim[3], len(list_obs_data)) list_duration_2 = random_action return [ ActData( phase_index_1=list_phase_1[i], duration_1=list_duration_1[i], phase_index_2=list_phase_2[i], duration_2=list_duration_2[i], ) for i in range(len(list_obs_data)) ] @predict_wrapper def predict(self, list_obs_data): return self.__predict_detail(list_obs_data, exploit_flag=False) @exploit_wrapper def exploit(self, observation): obs_data = self.observation_process(observation["obs"], observation["extra_info"]) if not obs_data: return [[None, None, None]] act_data = self.__predict_detail([obs_data], exploit_flag=True) act = self.action_process(act_data[0]) return act @learn_wrapper def learn(self, list_sample_data): return self.algorithm.learn(list_sample_data) @save_model_wrapper def save_model(self, path=None, id="1"): # To save the model, it can consist of multiple files, # and it is important to ensure that each filename includes the "model.ckpt-id" field. # 保存模型, 可以是多个文件, 需要确保每个文件名里包括了model.ckpt-id字段 model_file_path = f"{path}/model.ckpt-{str(id)}.pkl" # Copy the model's state dictionary to the CPU # 将模型的状态字典拷贝到CPU model_state_dict = self.model.state_dict() model_state_dict_cpu = {k: v.clone().cpu() for k, v in self.model.state_dict().items()} torch.save(model_state_dict_cpu, model_file_path) self.logger.info(f"save model {model_file_path} successfully") @load_model_wrapper def load_model(self, path=None, id="1"): # When loading the model, you can load multiple files, # and it is important to ensure that each filename matches the one used during the save_model process. # 加载模型, 可以加载多个文件, 注意每个文件名需要和save_model时保持一致 model_file_path = f"{path}/model.ckpt-{str(id)}.pkl" self.model.load_state_dict(torch.load(model_file_path, map_location=self.model.device)) self.logger.info(f"load model {model_file_path} successfully") def observation_process(self, obs, extra_info): # User-defined section, can record or update traffic information per frame. # 用户自定义部分, 可每帧对交通信息进行记录或更新 self.preprocess.update_traffic_info(obs, extra_info) frame_state = obs["framestate"] # Parse frame_state # 解析 frame_state frame_no, frame_time, vehicles = ( frame_state["frame_no"], frame_state["frame_time"], frame_state["vehicles"], ) phases = frame_state["phases"] junction_1 = phases[1] junction_2 = phases[2] jct_phase_1 = [ 0, ] * 8 jct_phase_2 = [ 0, ] * 6 jct_phase_1[junction_1["phase_id"]] = 1 jct_phase_2[junction_2["phase_id"]] = 1 # Divide the lane into several grids along the lane direction and the vehicle driving direction # 沿车道方向和车辆行驶方向将车道划分为数个栅格 speed_dict = np.zeros((Config.GRID_WIDTH, Config.GRID_NUM)) position_dict = np.zeros((Config.GRID_WIDTH, Config.GRID_NUM)) # The default value of junction_id in a double intersection scenario are 1 and 2 # 本场景交叉口junction_id十字路口为1、T字路口为2 junction_ids = [1, 2] # Initialize state-related variables to prevent errors when there are no vehicles in the traffic scenario # 初始化状态相关变量, 防止交通场景内车辆为空时报错 position = list(position_dict.astype(int).flatten()) speed = list(speed_dict.flatten()) for vehicle in vehicles: # Only count vehicles on the enter lane # 仅统计位于进口车道上的车辆信息 if on_enter_lane(vehicle): # Convert the vehicle x,y coordinates to grid coordinates. Here, # get_lane_code maps the lane number to integers 0-25, corresponding to 26 import lanes # 将车辆x,y坐标转化为栅格坐标, 此处get_lane_code将车道编号映射至整数0-25, 对应26条进口车道 x_pos = get_lane_code(vehicle) y_pos = int((vehicle["position_in_lane"]["y"] / 1) // Config.GRID_LENGTH) if y_pos >= Config.GRID_NUM: continue if vehicle["target_junction"] not in junction_ids: continue speed_dict[x_pos, y_pos] = float( vehicle["speed"] / self.preprocess.vehicle_configs[vehicle["v_config_id"]]["max_speed"] ) position_dict[x_pos, y_pos] = 1 else: continue position = list(position_dict.astype(int).flatten()) speed = list(speed_dict.flatten()) # Integrate all state quantities into the feature # 将所有状态量整合在feature中 feature = position + speed + [obs["legal_action"][1], obs["legal_action"][2]] + jct_phase_1 + jct_phase_2 return ObsData(feature=feature) def action_process(self, act_data): # Action of env follow [[junction_id, phase, duration], [junction_id, phase, duration]] format, you can map agent's action to environment's action # 环境可采纳的动作格式为 [[junction_id, phase, duration], [junction_id, phase, duration]],由于智能体返回的动作一般从0开始,duration从1开始,可以做一个映射 phase_index_1 = act_data.phase_index_1 duration_1 = act_data.duration_1 + 1 phase_index_2 = act_data.phase_index_2 duration_2 = act_data.duration_2 + 1 action = [[1, phase_index_1, duration_1], [2, phase_index_2, duration_2]] return action检测此智能体是否合理,修正一下
08-30
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值