GX
This commit is contained in:
@@ -30,4 +30,12 @@ public interface ZheDaAgvService {
|
||||
|
||||
public HttpResponse markComplete(String code);
|
||||
|
||||
/**
|
||||
* 删除先知任务
|
||||
*
|
||||
* @param instCode
|
||||
* @return
|
||||
*/
|
||||
public HttpResponse deleteZDAgvInst(String instCode);
|
||||
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@ import org.springframework.stereotype.Service;
|
||||
@RequiredArgsConstructor
|
||||
public class ZheDaAgvServiceImpl implements ZheDaAgvService {
|
||||
private final ParamService paramService;
|
||||
private final TaskService taskService;
|
||||
private final InstructionService instructionService;
|
||||
@LokiLog(type = LokiLogType.AGV)
|
||||
@Override
|
||||
public HttpResponse sendAgvInstToAgv(Instruction inst) throws Exception {
|
||||
@@ -262,7 +264,7 @@ public class ZheDaAgvServiceImpl implements ZheDaAgvService {
|
||||
//2楼AGV起点追加任务
|
||||
String start_device_code = respjson.getString("device_code");
|
||||
String next_device_code = inst.getNext_device_code();
|
||||
destinations.add(destination(start_device_code, "load", "1", "1"));
|
||||
destinations.add(destination(start_device_code, "Load", "1", "1"));
|
||||
destinations.add(destination(next_device_code, "Unload", "1", "1"));
|
||||
|
||||
} else if(StrUtil.equals(inst.getInstruction_type(),"4")){
|
||||
@@ -273,11 +275,11 @@ public class ZheDaAgvServiceImpl implements ZheDaAgvService {
|
||||
|
||||
jo.put("destinations", destinations);
|
||||
|
||||
String agvurl = paramService.findByCode(AcsConfig.AGVURL).getValue();
|
||||
String agvport = paramService.findByCode(AcsConfig.AGVPORT).getValue();
|
||||
String agvurl = paramService.findByCode(AcsConfig.AGVURL2).getValue();
|
||||
String agvport = paramService.findByCode(AcsConfig.AGVPORT2).getValue();
|
||||
|
||||
String url = agvurl + ":" + agvport + "/rmds/addDestinations";
|
||||
log.info("下发agv任务请求:{}", url);
|
||||
String url = agvurl + ":" + agvport + "/rmds/v1/addDestinations";
|
||||
log.info("下发追加agv任务请求:{}", jo);
|
||||
|
||||
HttpResponse result = HttpRequest.post(url)
|
||||
.body(String.valueOf(jo))//表单内容
|
||||
@@ -289,14 +291,14 @@ public class ZheDaAgvServiceImpl implements ZheDaAgvService {
|
||||
JSONObject complete = new JSONObject();
|
||||
complete.put("task_code", inst.getInstruction_code());
|
||||
|
||||
String url2 = agvurl + ":" + agvport + "markComplete";
|
||||
String url2 = agvurl + ":" + agvport + "/rmds/v1/markComplete";
|
||||
log.info("下发agv任务请求:{}", url2);
|
||||
|
||||
HttpResponse result2 = HttpRequest.post(url2)
|
||||
.body(String.valueOf(complete))//表单内容
|
||||
.timeout(20000)//超时,毫秒
|
||||
.execute();
|
||||
log.info("下发agv任务请求反馈:{}", result2);
|
||||
log.info("下发agv任务封口请求反馈:{}", result2);
|
||||
}
|
||||
}
|
||||
//放货的进入前等待和离开等待
|
||||
@@ -402,13 +404,13 @@ public class ZheDaAgvServiceImpl implements ZheDaAgvService {
|
||||
log.info("反馈AGV请求数据:{}", requestjo);
|
||||
System.out.println("back agv:" + requestjo);
|
||||
|
||||
String agvurl = paramService.findByCode(AcsConfig.AGVURL).getValue();
|
||||
String agvport = paramService.findByCode(AcsConfig.AGVPORT).getValue();
|
||||
String agvurl = paramService.findByCode(AcsConfig.AGVURL2).getValue();
|
||||
String agvport = paramService.findByCode(AcsConfig.AGVPORT2).getValue();
|
||||
|
||||
if (inst.getInstruction_type().equals("4")) {
|
||||
agvurl = paramService.findByCode(AcsConfig.AGVURL2).getValue();
|
||||
if (inst.getInstruction_type().equals("1")) {
|
||||
agvurl = paramService.findByCode(AcsConfig.AGVURL).getValue();
|
||||
}
|
||||
agvurl = agvurl + ":" + agvport + "/v1/transportOrders/" + jobno + "/interact";
|
||||
agvurl = agvurl + ":" + agvport + "/rmds/v1/transportOrders/" + jobno + "/interact";
|
||||
|
||||
HttpResponse result = HttpRequest.post(agvurl)
|
||||
.body(String.valueOf(requestjo))
|
||||
@@ -555,5 +557,27 @@ public class ZheDaAgvServiceImpl implements ZheDaAgvService {
|
||||
return destinationOrder;
|
||||
}
|
||||
|
||||
@LokiLog(type = LokiLogType.AGV)
|
||||
@Override
|
||||
public HttpResponse deleteZDAgvInst(String instCode) {
|
||||
if (StrUtil.equals(paramService.findByCode(AcsConfig.FORKAGV).getValue(), "1")) {
|
||||
String agvurl = paramService.findByCode(AcsConfig.AGVURL2).getValue();
|
||||
String agvport = paramService.findByCode(AcsConfig.AGVPORT2).getValue();
|
||||
Instruction instruction = instructionService.findByCode(instCode);
|
||||
if (instruction.getInstruction_type().equals("1")){
|
||||
agvurl = paramService.findByCode(AcsConfig.AGVURL).getValue();
|
||||
}
|
||||
agvurl = agvurl + ":" + agvport + "/rmds/v1/transportOrders/" + instCode + "/withdrawal";
|
||||
log.info("删除agv指令请求agvurl:{}", agvurl);
|
||||
HttpResponse result = HttpRequest.post(agvurl)
|
||||
.body("{}")
|
||||
.timeout(20000)//超时,毫秒
|
||||
.execute();
|
||||
log.info("删除agv指令请求反馈:{}", result);
|
||||
return result;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -383,12 +383,16 @@ public class InstructionServiceImpl implements InstructionService, ApplicationAu
|
||||
if (startdevice.getDeviceDriver() instanceof EmptyVehicleStackingPositionDeviceDriver) {
|
||||
emptyVehicleStackingPositionDeviceDriver = (EmptyVehicleStackingPositionDeviceDriver) startdevice.getDeviceDriver();
|
||||
//已经生成指令得取空托盘数量
|
||||
Integer emptyNum = taskService.queryStartNum(dto.getStart_device_code());
|
||||
Integer emptyNum = taskService.queryStartNum(dto.getStart_device_code(), dto.getNext_device_code());
|
||||
//当前任务取得空托盘数量
|
||||
Integer now_need_emptypallet_num = Integer.parseInt(taskService.findByCode(dto.getTask_code()).getEmptypallet_num());
|
||||
//当前叠盘架有空托盘数量
|
||||
int now_number = emptyVehicleStackingPositionDeviceDriver.getNumber();
|
||||
//判断从第几层取
|
||||
int i = now_number - now_need_emptypallet_num - emptyNum + 1;
|
||||
if (i <= 0) {
|
||||
throw new BadRequestException("当前叠盘架:" + dto.getStart_device_code() + "叠盘数为0!");
|
||||
}
|
||||
dto.setStart_device_code(start_device_code + "." + i);
|
||||
dto.setStart_parent_code(start_device_code + "." + i);
|
||||
dto.setStart_point_code(start_device_code + "." + i);
|
||||
@@ -396,12 +400,16 @@ public class InstructionServiceImpl implements InstructionService, ApplicationAu
|
||||
if (nextdevice.getDeviceDriver() instanceof EmptyVehicleStackingPositionDeviceDriver) {
|
||||
emptyVehicleStackingPositionDeviceDriver = (EmptyVehicleStackingPositionDeviceDriver) startdevice.getDeviceDriver();
|
||||
//已经生成指令得放空托盘数量
|
||||
Integer emptyNum = taskService.queryEndNum(dto.getStart_device_code());
|
||||
Integer emptyNum = taskService.queryEndNum(dto.getStart_device_code(), dto.getNext_device_code());
|
||||
//当前任务放空托盘数量
|
||||
Integer now_need_emptypallet_num = Integer.parseInt(taskService.findByCode(dto.getTask_code()).getEmptypallet_num());
|
||||
//当前叠盘架有空托盘数量
|
||||
int now_number = emptyVehicleStackingPositionDeviceDriver.getNumber();
|
||||
int i = now_number + emptyNum + 1;
|
||||
//判断当前放叠盘架位置叠盘数量+已经生成指令的需要搬运的叠盘数量是否大于最大叠盘数量
|
||||
if (i - 1 + now_need_emptypallet_num > 12) {
|
||||
throw new BadRequestException("当前叠盘架:" + dto.getStart_device_code() + "超出最大叠盘数量!");
|
||||
}
|
||||
dto.setNext_device_code(next_device_code + "." + i);
|
||||
dto.setNext_parent_code(next_device_code + "." + i);
|
||||
dto.setNext_point_code(next_device_code + "." + i);
|
||||
@@ -992,6 +1000,11 @@ public class InstructionServiceImpl implements InstructionService, ApplicationAu
|
||||
XianGongAgvService xianGongAgvService = SpringContextHolder.getBean(XianGongAgvService.class);
|
||||
xianGongAgvService.deleteXZAgvInst(entity.getInstruction_code());
|
||||
flag = true;
|
||||
} else if (StrUtil.equals(paramService.findByCode(AcsConfig.AGVTYPE).getValue(), "4")
|
||||
&& !StrUtil.equals(entity.getSend_status(), "2")) {
|
||||
ZheDaAgvService zheDaAgvService = SpringContextHolder.getBean(ZheDaAgvService.class);
|
||||
zheDaAgvService.deleteZDAgvInst(entity.getInstruction_code());
|
||||
flag = true;
|
||||
} else {
|
||||
flag = true;
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ public interface TaskService {
|
||||
* @param code
|
||||
* @return
|
||||
*/
|
||||
Integer queryStartNum(String code);
|
||||
Integer queryStartNum(String start_code,String next_code);
|
||||
|
||||
/**
|
||||
* 查询叠盘位起点未执行完任务得数量叠盘数量
|
||||
@@ -274,7 +274,7 @@ public interface TaskService {
|
||||
* @param code
|
||||
* @return
|
||||
*/
|
||||
Integer queryEndNum(String code);
|
||||
Integer queryEndNum(String start_code,String next_code);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1215,12 +1215,12 @@ public class TaskServiceImpl implements TaskService, ApplicationAutoInitial {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer queryStartNum(String code) {
|
||||
public Integer queryStartNum(String start_code,String next_code) {
|
||||
int num = 0;
|
||||
Iterator<TaskDto> iterator = tasks.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
TaskDto task = iterator.next();
|
||||
if (task.getStart_device_code().equals(code) && task.getTask_status().equals("1")) {
|
||||
if (task.getStart_device_code().equals(start_code) && !task.getNext_device_code().equals(next_code) && task.getTask_status().equals("1")) {
|
||||
num = num + Integer.parseInt(task.getEmptypallet_num());
|
||||
}
|
||||
}
|
||||
@@ -1228,12 +1228,12 @@ public class TaskServiceImpl implements TaskService, ApplicationAutoInitial {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer queryEndNum(String code) {
|
||||
public Integer queryEndNum(String next_code,String start_code) {
|
||||
int num = 0;
|
||||
Iterator<TaskDto> iterator = tasks.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
TaskDto task = iterator.next();
|
||||
if (task.getNext_device_code().equals(code) && task.getTask_status().equals("1")) {
|
||||
if (task.getNext_device_code().equals(next_code) && !task.getStart_device_code().equals(start_code) && task.getTask_status().equals("1")) {
|
||||
num = num + Integer.parseInt(task.getEmptypallet_num());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,26 +31,27 @@ public class QueryOneZDAgvTaskStatus {
|
||||
|
||||
|
||||
public void run() throws Exception {
|
||||
HttpResponse response = agvService.queryAgvInstStatus("1");
|
||||
//查询AGV指令列表
|
||||
JSONArray inst_rows = JSONArray.parseArray(response.body());
|
||||
for (int i = 0; i < inst_rows.size(); i++) {
|
||||
JSONObject inst_jo = inst_rows.getJSONObject(i);
|
||||
String inst_code = inst_jo.getString("task_code");
|
||||
Instruction inst = instructionService.findByCodeFromCache(inst_code);
|
||||
if (ObjectUtil.isEmpty(inst)){
|
||||
continue;
|
||||
}
|
||||
//反馈结果状态
|
||||
log.info("instcode:" + inst_code + "," + inst_jo.toString());
|
||||
try {
|
||||
HttpResponse response = agvService.queryAgvInstStatus("1");
|
||||
//查询AGV指令列表
|
||||
JSONArray inst_rows = JSONArray.parseArray(response.body());
|
||||
for (int i = 0; i < inst_rows.size(); i++) {
|
||||
JSONObject inst_jo = inst_rows.getJSONObject(i);
|
||||
String inst_code = inst_jo.getString("task_code");
|
||||
Instruction inst = instructionService.findByCodeFromCache(inst_code);
|
||||
if (ObjectUtil.isEmpty(inst)){
|
||||
continue;
|
||||
}
|
||||
//反馈结果状态
|
||||
log.info("instcode:" + inst_code + "," + inst_jo.toString());
|
||||
|
||||
String state = inst_jo.getString("state");
|
||||
String vehicle = "";
|
||||
//正在执行指令agv车号
|
||||
if (!StrUtil.isEmpty(inst_jo.getString("vehicle"))) {
|
||||
vehicle = inst_jo.getString("vehicle");
|
||||
inst.setCarno(vehicle);
|
||||
}
|
||||
String state = inst_jo.getString("state");
|
||||
String vehicle = "";
|
||||
//正在执行指令agv车号
|
||||
if (!StrUtil.isEmpty(inst_jo.getString("vehicle"))) {
|
||||
vehicle = inst_jo.getString("vehicle");
|
||||
inst.setCarno(vehicle);
|
||||
}
|
||||
// RAW:初始状态
|
||||
// ACTIVE:业务订单已激活
|
||||
// DISPATCHABLE:业务订单已通过系统验证,等待被调度执行
|
||||
@@ -60,40 +61,44 @@ public class QueryOneZDAgvTaskStatus {
|
||||
// FAILED:业务订单已失败
|
||||
// UNROUTABLE:无法规划该业务订单的执行路线
|
||||
|
||||
//执行中
|
||||
if ("BEING_PROCESSED".equals(state)) {
|
||||
if (inst != null) {
|
||||
inst.setInstruction_status("1");
|
||||
instructionService.update(inst);
|
||||
//执行中
|
||||
if ("BEING_PROCESSED".equals(state)) {
|
||||
if (inst != null) {
|
||||
inst.setInstruction_status("1");
|
||||
instructionService.update(inst);
|
||||
}
|
||||
} else if ("FINISHED".equals(state)) {
|
||||
if (inst != null) {
|
||||
inst.setInstruction_status("2");
|
||||
instructionService.finish(inst);
|
||||
}
|
||||
} else if ("WITHDRAWN".equals(state) || "FAILED".equals(state)) {
|
||||
if (inst != null) {
|
||||
inst.setInstruction_status("3");
|
||||
instructionService.update(inst);
|
||||
instructionService.removeByCodeFromCache(inst_jo.getString("task_code"));
|
||||
}
|
||||
}
|
||||
} else if ("FINISHED".equals(state)) {
|
||||
if (inst != null) {
|
||||
inst.setInstruction_status("2");
|
||||
instructionService.finish(inst);
|
||||
}
|
||||
} else if ("WITHDRAWN".equals(state) || "FAILED".equals(state)) {
|
||||
if (inst != null) {
|
||||
inst.setInstruction_status("3");
|
||||
instructionService.update(inst);
|
||||
instructionService.removeByCodeFromCache(inst_jo.getString("task_code"));
|
||||
}
|
||||
}
|
||||
JSONArray ja = inst_jo.getJSONArray("destinations");
|
||||
for (int j = 0; j < ja.size(); j++) {
|
||||
JSONObject jo = ja.getJSONObject(j);
|
||||
JSONArray pro_rows = jo.getJSONArray("properties");
|
||||
//Load 取货动作 Unload放货动作 Wait等待
|
||||
String operation = jo.getString("operation");
|
||||
String device = jo.getString("locationName");
|
||||
for (int k = 0; k < pro_rows.size(); k++) {
|
||||
JSONObject item = pro_rows.getJSONObject(k);
|
||||
if ("True".equals(item.get("value"))) {
|
||||
String param = item.get("key").toString();
|
||||
//EntryRequired是否进入前等待 PauseOnStation是否离开等待 Wait在该点进行等待
|
||||
agvService.process(inst_code, param, device, operation, vehicle);
|
||||
JSONArray ja = inst_jo.getJSONArray("destinations");
|
||||
for (int j = 0; j < ja.size(); j++) {
|
||||
JSONObject jo = ja.getJSONObject(j);
|
||||
JSONArray pro_rows = jo.getJSONArray("properties");
|
||||
//Load 取货动作 Unload放货动作 Wait等待
|
||||
String operation = jo.getString("operation");
|
||||
String device = jo.getString("locationName");
|
||||
for (int k = 0; k < pro_rows.size(); k++) {
|
||||
JSONObject item = pro_rows.getJSONObject(k);
|
||||
if ("True".equals(item.get("value"))) {
|
||||
String param = item.get("key").toString();
|
||||
//EntryRequired是否进入前等待 PauseOnStation是否离开等待 Wait在该点进行等待
|
||||
agvService.process(inst_code, param, device, operation, vehicle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}catch (Exception e){
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,26 +27,27 @@ public class QueryTwoZDAgvTaskStatus {
|
||||
|
||||
|
||||
public void run() throws Exception {
|
||||
HttpResponse response2 = agvService.queryAgvInstStatus("2");
|
||||
//查询AGV指令列表
|
||||
JSONArray inst_rows2 = JSONArray.parseArray(response2.body());
|
||||
for (int i = 0; i < inst_rows2.size(); i++) {
|
||||
JSONObject inst_jo = inst_rows2.getJSONObject(i);
|
||||
String inst_code = inst_jo.getString("task_code");
|
||||
Instruction inst = instructionService.findByCodeFromCache(inst_code);
|
||||
if (ObjectUtil.isEmpty(inst)){
|
||||
continue;
|
||||
}
|
||||
//反馈结果状态
|
||||
log.info("instcode:" + inst_code + "," + inst_jo.toString());
|
||||
try {
|
||||
HttpResponse response2 = agvService.queryAgvInstStatus("2");
|
||||
//查询AGV指令列表
|
||||
JSONArray inst_rows2 = JSONArray.parseArray(response2.body());
|
||||
for (int i = 0; i < inst_rows2.size(); i++) {
|
||||
JSONObject inst_jo = inst_rows2.getJSONObject(i);
|
||||
String inst_code = inst_jo.getString("task_code");
|
||||
Instruction inst = instructionService.findByCodeFromCache(inst_code);
|
||||
if (ObjectUtil.isEmpty(inst)){
|
||||
continue;
|
||||
}
|
||||
//反馈结果状态
|
||||
log.info("instcode:" + inst_code + "," + inst_jo.toString());
|
||||
|
||||
String status = inst_jo.getString("status");
|
||||
String vehicle = "";
|
||||
//正在执行指令agv车号
|
||||
if (!StrUtil.isEmpty(inst_jo.getString("vehicle"))) {
|
||||
vehicle = inst_jo.getString("vehicle");
|
||||
inst.setCarno(vehicle);
|
||||
}
|
||||
String status = inst_jo.getString("status");
|
||||
String vehicle = "";
|
||||
//正在执行指令agv车号
|
||||
if (!StrUtil.isEmpty(inst_jo.getString("vehicle"))) {
|
||||
vehicle = inst_jo.getString("vehicle");
|
||||
inst.setCarno(vehicle);
|
||||
}
|
||||
// RAW:初始状态
|
||||
// ACTIVE:业务订单已激活
|
||||
// DISPATCHABLE:业务订单已通过系统验证,等待被调度执行
|
||||
@@ -56,40 +57,44 @@ public class QueryTwoZDAgvTaskStatus {
|
||||
// FAILED:业务订单已失败
|
||||
// UNROUTABLE:无法规划该业务订单的执行路线
|
||||
|
||||
//执行中
|
||||
if ("BEING_PROCESSED".equals(status) || "ACTIVE".equals(status)) {
|
||||
if (inst != null) {
|
||||
inst.setInstruction_status("1");
|
||||
instructionService.update(inst);
|
||||
//执行中
|
||||
if ("BEING_PROCESSED".equals(status) || "ACTIVE".equals(status)) {
|
||||
if (inst != null) {
|
||||
inst.setInstruction_status("1");
|
||||
instructionService.update(inst);
|
||||
}
|
||||
} else if ("FINISHED".equals(status)) {
|
||||
if (inst != null) {
|
||||
inst.setInstruction_status("2");
|
||||
instructionService.finish(inst);
|
||||
}
|
||||
} else if ("WITHDRAWN".equals(status) || "FAILED".equals(status)) {
|
||||
if (inst != null) {
|
||||
inst.setInstruction_status("3");
|
||||
instructionService.update(inst);
|
||||
instructionService.removeByCodeFromCache(inst_jo.getString("task_code"));
|
||||
}
|
||||
}
|
||||
} else if ("FINISHED".equals(status)) {
|
||||
if (inst != null) {
|
||||
inst.setInstruction_status("2");
|
||||
instructionService.finish(inst);
|
||||
}
|
||||
} else if ("WITHDRAWN".equals(status) || "FAILED".equals(status)) {
|
||||
if (inst != null) {
|
||||
inst.setInstruction_status("3");
|
||||
instructionService.update(inst);
|
||||
instructionService.removeByCodeFromCache(inst_jo.getString("task_code"));
|
||||
}
|
||||
}
|
||||
JSONArray ja = inst_jo.getJSONArray("destinations");
|
||||
for (int j = 0; j < ja.size(); j++) {
|
||||
JSONObject jo = ja.getJSONObject(j);
|
||||
JSONArray pro_rows = jo.getJSONArray("properties");
|
||||
//Load 取货动作 Unload放货动作 Wait等待
|
||||
String operation = jo.getString("operation");
|
||||
String device = jo.getString("locationName");
|
||||
for (int k = 0; k < pro_rows.size(); k++) {
|
||||
JSONObject item = pro_rows.getJSONObject(k);
|
||||
if ("True".equals(item.get("value"))) {
|
||||
String param = item.get("key").toString();
|
||||
//EntryRequired是否进入前等待 PauseOnStation是否离开等待 Wait在该点进行等待
|
||||
agvService.process(inst_code, param, device, operation, vehicle);
|
||||
JSONArray ja = inst_jo.getJSONArray("destinations");
|
||||
for (int j = 0; j < ja.size(); j++) {
|
||||
JSONObject jo = ja.getJSONObject(j);
|
||||
JSONArray pro_rows = jo.getJSONArray("properties");
|
||||
//Load 取货动作 Unload放货动作 Wait等待
|
||||
String operation = jo.getString("operation");
|
||||
String device = jo.getString("locationName");
|
||||
for (int k = 0; k < pro_rows.size(); k++) {
|
||||
JSONObject item = pro_rows.getJSONObject(k);
|
||||
if ("true".equals(item.get("value"))) {
|
||||
String param = item.get("key").toString();
|
||||
//EntryRequired是否进入前等待 PauseOnStation是否离开等待 Wait在该点进行等待
|
||||
agvService.process(inst_code, param, device, operation, vehicle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e){
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user