Merge remote-tracking branch 'origin/test' into test
This commit is contained in:
commit
5a03ce0f71
@ -7,27 +7,14 @@ public enum DirectionLabelEnum {
|
||||
X,
|
||||
XF,
|
||||
XD,
|
||||
XN,
|
||||
XW,
|
||||
XWN,
|
||||
S,
|
||||
SF,
|
||||
SD,
|
||||
SDN,
|
||||
SH,
|
||||
SHN,
|
||||
NO;
|
||||
|
||||
public static DirectionLabelEnum getLabel(String label) {
|
||||
switch (label) {
|
||||
case "X":
|
||||
return DirectionLabelEnum.X;
|
||||
case "XF":
|
||||
return DirectionLabelEnum.XF;
|
||||
case "XD":
|
||||
return DirectionLabelEnum.XD;
|
||||
case "S":
|
||||
return DirectionLabelEnum.S;
|
||||
case "SF":
|
||||
return DirectionLabelEnum.SF;
|
||||
case "SD":
|
||||
return DirectionLabelEnum.SD;
|
||||
default:
|
||||
return DirectionLabelEnum.NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package club.joylink.rtss.entity;
|
||||
|
||||
import club.joylink.rtss.constants.DirectionLabelEnum;
|
||||
import club.joylink.rtss.simulation.cbtc.data.map.StationDirection;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import lombok.Getter;
|
||||
@ -58,6 +59,21 @@ public class DraftMapStationDirection {
|
||||
*/
|
||||
private String signalCode;
|
||||
|
||||
/**
|
||||
* 所属模式(自动闭塞、半自动闭塞)
|
||||
*/
|
||||
private StationDirection.DirectionRunModel runModel;
|
||||
|
||||
/**
|
||||
* 默认的接发状态
|
||||
*/
|
||||
private StationDirection.ReceiveAndDeliverModel runStatus;
|
||||
|
||||
/**
|
||||
* 相对方向中的编码
|
||||
*/
|
||||
private String relativeCode;
|
||||
|
||||
public void generateSectionList() {
|
||||
if (StringUtils.isEmpty(sectionsCode)) {
|
||||
sectionList = new ArrayList<>(0);
|
||||
|
@ -755,6 +755,11 @@ public class Operation {
|
||||
*/
|
||||
Train_Load_Trip_Number_Train,
|
||||
|
||||
/**
|
||||
* 大铁修改车次号
|
||||
*/
|
||||
Train_Update_Trip_Number_Train,
|
||||
|
||||
//--------------------------- 司机 ---------------------------
|
||||
/**
|
||||
* 改变列车的牵引/制动力
|
||||
|
@ -399,7 +399,15 @@ public class TrainOperateHandler {
|
||||
* 大铁加载,指定车次列车
|
||||
*/
|
||||
@OperateHandlerMapping(type = Operation.Type.Train_Load_Trip_Number_Train)
|
||||
public void loadServiceNumberTrain(Simulation simulation, String tripNumber, String sectionCode, boolean right) {
|
||||
atsTrainLoadService.loadTripNumberTrain(simulation, tripNumber, sectionCode, right);
|
||||
public void loadServiceNumberTrain(Simulation simulation, String tripNumber, String sectionCode) {
|
||||
atsTrainLoadService.loadTripNumberTrain(simulation, tripNumber, sectionCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 大铁修改车次号
|
||||
*/
|
||||
@OperateHandlerMapping(type = Operation.Type.Train_Update_Trip_Number_Train)
|
||||
public void updateServiceNumberTrain(Simulation simulation, String tripNumber, String groupNumber) {
|
||||
atsTrainLoadService.updateServiceNumberTrain(simulation, tripNumber, groupNumber);
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import club.joylink.rtss.simulation.cbtc.device.virtual.VRTrainRunningService;
|
||||
import club.joylink.rtss.simulation.cbtc.event.SimulationRunAsPlanEvent;
|
||||
import club.joylink.rtss.simulation.cbtc.exception.SimulationException;
|
||||
import club.joylink.rtss.simulation.cbtc.exception.SimulationExceptionType;
|
||||
import club.joylink.rtss.simulation.cbtc.onboard.ATP.ATPService;
|
||||
import club.joylink.rtss.simulation.cbtc.tool.DeviceStatusModifyTool;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -73,6 +74,9 @@ public class AtsTrainLoadService {
|
||||
@Autowired
|
||||
private CiApiService ciApiService;
|
||||
|
||||
@Autowired
|
||||
private ATPService atpService;
|
||||
|
||||
public int getGivenTimeCouldLoadedTrainNumber(Simulation simulation, LocalTime time) {
|
||||
List<TrainLoadParam2> list = this.getCurrentAllNeedLoadTrain(simulation, time);
|
||||
return list.size();
|
||||
@ -859,16 +863,12 @@ public class AtsTrainLoadService {
|
||||
* @param simulation 仿真实体
|
||||
* @param tripNumber 车次号
|
||||
* @param sectionCode 区段编号
|
||||
* @param right 上下行
|
||||
*/
|
||||
public void loadTripNumberTrain(Simulation simulation, String tripNumber, String sectionCode, boolean right) {
|
||||
public void loadTripNumberTrain(Simulation simulation, String tripNumber, String sectionCode) {
|
||||
SimulationDataRepository repository = simulation.getRepository();
|
||||
boolean isExist = repository.getTrainInfoMap().values().stream()
|
||||
.anyMatch(trainInfo -> tripNumber.equals(trainInfo.getTripNumber()));
|
||||
if (isExist) {
|
||||
throw new SimulationException(SimulationExceptionType.Operation_Handle_FAIL,
|
||||
String.format("车次[%s]的列车已经在运行中", tripNumber));
|
||||
}
|
||||
validServiceNumber(simulation, tripNumber);
|
||||
// 最后一位数字:偶数上行、奇数下行
|
||||
boolean right = (tripNumber.charAt(tripNumber.length() - 1) - 48) % 2 == 0;
|
||||
Optional<VirtualRealityTrain> virtualRealityTrainOptional = repository.getAllVrTrain().stream()
|
||||
.filter(trainInfo -> !repository.isVrTrainOnline(trainInfo.getGroupNumber()))
|
||||
.findFirst();
|
||||
@ -881,6 +881,53 @@ public class AtsTrainLoadService {
|
||||
trainOnline(simulation, sectionCode, right, repository, virtualRealityTrain);
|
||||
}
|
||||
|
||||
/**
|
||||
* 大铁修改车次列车
|
||||
*
|
||||
* @param simulation 仿真实体
|
||||
* @param tripNumber 车次号
|
||||
* @param groupNumber 车组号
|
||||
*/
|
||||
public void updateServiceNumberTrain(Simulation simulation, String tripNumber, String groupNumber) {
|
||||
SimulationDataRepository repository = simulation.getRepository();
|
||||
validServiceNumber(simulation, tripNumber);
|
||||
if (!repository.isVrTrainOnline(groupNumber)) {
|
||||
throw new SimulationException(SimulationExceptionType.Operation_Handle_FAIL, String.format("车组号[%s]的列车不存在", groupNumber));
|
||||
}
|
||||
VirtualRealityTrain virtualRealityTrain = repository.getVRByCode(groupNumber, VirtualRealityTrain.class);
|
||||
// 最后一位数字:偶数上行、奇数下行
|
||||
boolean right = (tripNumber.charAt(tripNumber.length() - 1) - 48) % 2 == 0;
|
||||
if (!Objects.equals(right, virtualRealityTrain.isRight())) {
|
||||
atpService.turnDirectionImmediately(virtualRealityTrain);
|
||||
}
|
||||
virtualRealityTrain.setTripNumber(tripNumber);
|
||||
virtualRealityTrain.setRight(right);
|
||||
TrainInfo trainInfo = TrainInfo.constructManualTrain(virtualRealityTrain);
|
||||
trainInfo.tracking(virtualRealityTrain);
|
||||
repository.addTrainInfo(trainInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 大铁操作车次前验证
|
||||
*
|
||||
* @param simulation 仿真实体
|
||||
* @param tripNumber 车次号
|
||||
*/
|
||||
private void validServiceNumber(Simulation simulation, String tripNumber) {
|
||||
SimulationDataRepository repository = simulation.getRepository();
|
||||
boolean isExist = repository.getTrainInfoMap().values().stream()
|
||||
.anyMatch(trainInfo -> tripNumber.equals(trainInfo.getTripNumber()));
|
||||
if (isExist) {
|
||||
throw new SimulationException(SimulationExceptionType.Operation_Handle_FAIL,
|
||||
String.format("车次[%s]的列车已经在运行中", tripNumber));
|
||||
}
|
||||
int lastNum = tripNumber.charAt(tripNumber.length() - 1) - 48;
|
||||
if (lastNum < 0 && lastNum > 9) {
|
||||
throw new SimulationException(SimulationExceptionType.Operation_Handle_FAIL,
|
||||
String.format("车次[%s]要以数字结尾", tripNumber));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 列车上线并构建ATS监控列车信息
|
||||
*
|
||||
|
@ -394,7 +394,10 @@ public class StationDirectionService {
|
||||
* @return 相对方向
|
||||
*/
|
||||
private StationDirection getRelativeStationDirection(Simulation simulation, StationDirection stationDirection) {
|
||||
StationDirection relativeStationDirection = null;
|
||||
StationDirection relativeStationDirection = stationDirection.getRelativeStationDirection();
|
||||
if (relativeStationDirection != null) {
|
||||
return relativeStationDirection;
|
||||
}
|
||||
// 获取邻站实体
|
||||
Optional<Station> adjacentStationOptional = getAdjacentStationOptional(simulation, stationDirection);
|
||||
if (adjacentStationOptional.isPresent()) {
|
||||
|
@ -188,7 +188,7 @@ public class CiApiServiceImpl2 implements CiApiService {
|
||||
station.getStationDirectionMap().values().stream()
|
||||
.filter(stationDirection -> route.getStart().getCode().equals(stationDirection.getSignal().getCode())
|
||||
|| route.getDestination().getCode().equals(stationDirection.getSignal().getCode()))
|
||||
.forEach(stationDirection -> stationDirection.modifyRunStatus(route))
|
||||
.forEach(stationDirection -> stationDirection.modifyRunStatus())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1633,16 +1633,11 @@ public class InterlockBuilder2 {
|
||||
if (elementMap.containsKey(stationDirection.getCode())) {
|
||||
errMsgList.add("已经存在车站运行方向" + stationDirection.getCode());
|
||||
} else {
|
||||
StationDirection model = new StationDirection(stationDirection.getCode(), stationDirection.getCode(), stationDirection.getLabelEnum());
|
||||
Station station = (Station) elementMap.get(stationDirection.getStationCode());
|
||||
model.setStation(station);
|
||||
// 注入区间数据
|
||||
if (!CollectionUtils.isEmpty(stationDirection.getSectionList())) {
|
||||
List<Section> sectionList = stationDirection.getSectionList()
|
||||
.stream()
|
||||
.map(sectionCode -> (Section) elementMap.get(sectionCode))
|
||||
.collect(Collectors.toList());
|
||||
model.setSectionList(sectionList);
|
||||
StationDirection model;
|
||||
if (stationDirection.getRunStatus() != null) {
|
||||
model = new StationDirection(stationDirection);
|
||||
} else {
|
||||
model = new StationDirection(stationDirection.getCode(), stationDirection.getCode(), stationDirection.getLabelEnum());
|
||||
}
|
||||
// 注入信号灯数据
|
||||
if (!StringUtils.isEmpty(stationDirection.getSignalCode())) {
|
||||
@ -1657,10 +1652,28 @@ public class InterlockBuilder2 {
|
||||
model.getDeliverRouteList().add((Route) elementMap.get(code));
|
||||
});
|
||||
}
|
||||
// 注入区间数据
|
||||
if (!CollectionUtils.isEmpty(stationDirection.getSectionList())) {
|
||||
List<Section> sectionList = stationDirection.getSectionList().stream()
|
||||
.map(sectionCode -> (Section) elementMap.get(sectionCode))
|
||||
.collect(Collectors.toList());
|
||||
model.setSectionList(sectionList);
|
||||
}
|
||||
model.modifyRunStatus();
|
||||
Station station = (Station) elementMap.get(stationDirection.getStationCode());
|
||||
model.setStation(station);
|
||||
station.getStationDirectionMap().put(model.getLabelEnum(), model);
|
||||
elementMap.put(model.getCode(), model);
|
||||
}
|
||||
});
|
||||
// 相对方向
|
||||
stationDirectionList.stream()
|
||||
.filter(draftMapStationDirection -> elementMap.containsKey(draftMapStationDirection.getRelativeCode()))
|
||||
.forEach(draftMapStationDirection -> {
|
||||
StationDirection stationDirection = (StationDirection) elementMap.get(draftMapStationDirection.getCode());
|
||||
StationDirection relativeStationDirection = (StationDirection) elementMap.get(draftMapStationDirection.getRelativeCode());
|
||||
stationDirection.setRelativeStationDirection(relativeStationDirection);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -578,7 +578,6 @@ public class CommandBO {
|
||||
Section section = train.getHeadPosition().getSection(); // 列车车头所在区段
|
||||
boolean loop = true;
|
||||
Signal signal;
|
||||
Switch switchObj;
|
||||
Section nextSection = section, targetSection = null;
|
||||
while (loop) {
|
||||
signal = isRight ? nextSection.getSignalToRight() : nextSection.getSignalToLeft(); // 方向信号机
|
||||
@ -611,6 +610,24 @@ public class CommandBO {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 大铁、停车
|
||||
*/
|
||||
Parking_Train(List.of(), SimulationMember.Type.DRIVER) {
|
||||
@Override
|
||||
public List<Step> buildStepList(Simulation simulation, SimulationMember targetMember, Map<String, Object> params) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Step execute(Simulation simulation, CommandBO command) {
|
||||
SimulationMember targetMember = command.getTargetMember();
|
||||
targetMember.setCommand(null);
|
||||
// 设置目标位置
|
||||
return buildDriveStep(null);
|
||||
}
|
||||
};
|
||||
|
||||
public enum ParamName {
|
||||
|
@ -2,6 +2,7 @@ package club.joylink.rtss.simulation.cbtc.data.map;
|
||||
|
||||
|
||||
import club.joylink.rtss.constants.DirectionLabelEnum;
|
||||
import club.joylink.rtss.entity.DraftMapStationDirection;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -39,6 +40,11 @@ public class StationDirection extends MapNamedElement {
|
||||
*/
|
||||
private DirectionLabelEnum labelEnum;
|
||||
|
||||
/**
|
||||
* 相对运行方向
|
||||
*/
|
||||
private StationDirection relativeStationDirection;
|
||||
|
||||
/**
|
||||
* 指示灯实体
|
||||
*/
|
||||
@ -153,6 +159,14 @@ public class StationDirection extends MapNamedElement {
|
||||
this.currentRouteList = getNowRouteList();
|
||||
}
|
||||
|
||||
public StationDirection(DraftMapStationDirection draftMapStationDirection) {
|
||||
this(draftMapStationDirection.getCode(), draftMapStationDirection.getCode());
|
||||
this.labelEnum = draftMapStationDirection.getLabelEnum();
|
||||
this.runModel = draftMapStationDirection.getRunModel();
|
||||
this.defaultRunStatus = draftMapStationDirection.getRunStatus();
|
||||
this.runStatus = draftMapStationDirection.getRunStatus();
|
||||
}
|
||||
|
||||
public StationDirection(String code, String name) {
|
||||
super(code, name, DeviceType.STATION_DIRECTION);
|
||||
setDefaultAttribute();
|
||||
@ -183,7 +197,7 @@ public class StationDirection extends MapNamedElement {
|
||||
/**
|
||||
* 修改当前运行状态
|
||||
*/
|
||||
public void modifyRunStatus(Route route) {
|
||||
public void modifyRunStatus() {
|
||||
// 自动模式
|
||||
if (DirectionRunModel.A.equals(this.runModel)) {
|
||||
this.currentRouteList = getNowRouteList();
|
||||
|
@ -9,6 +9,9 @@
|
||||
<result column="sections_code" jdbcType="VARCHAR" property="sectionsCode"/>
|
||||
<result column="label_enum" jdbcType="VARCHAR" property="labelEnum"/>
|
||||
<result column="signal_code" jdbcType="VARCHAR" property="signalCode"/>
|
||||
<result column="run_model" jdbcType="VARCHAR" property="runModel"/>
|
||||
<result column="run_status" jdbcType="VARCHAR" property="runStatus"/>
|
||||
<result column="relative_code" jdbcType="VARCHAR" property="relativeCode"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="Example_Where_Clause">
|
||||
@ -74,7 +77,8 @@
|
||||
</sql>
|
||||
|
||||
<sql id="Base_Column_List">
|
||||
id, map_id, code, station_code, sections_code, label_enum, signal_code
|
||||
id, map_id, code, station_code, sections_code, label_enum,
|
||||
signal_code, run_model, run_status, relative_code
|
||||
</sql>
|
||||
|
||||
<select id="selectByExample" parameterType="club.joylink.rtss.entity.DraftMapStationDirectionExample"
|
||||
@ -123,25 +127,28 @@
|
||||
parameterType="club.joylink.rtss.entity.DraftMapStationDirection"
|
||||
useGeneratedKeys="true">
|
||||
insert into draft_map_station_direction_label (
|
||||
map_id, code, station_code, sections_code, label_enum, signal_code
|
||||
map_id, code, station_code, sections_code, label_enum
|
||||
,signal_code, run_model, run_status, relative_code
|
||||
)
|
||||
values (
|
||||
#{mapId,jdbcType=BIGINT}, #{code,jdbcType=VARCHAR}, #{stationCode,jdbcType=VARCHAR},
|
||||
#{sectionsCode,jdbcType=VARCHAR},
|
||||
#{labelEnum,jdbcType=VARCHAR},#{signalCode,jdbcType=VARCHAR}
|
||||
#{mapId,jdbcType=BIGINT}, #{code,jdbcType=VARCHAR}, #{stationCode,jdbcType=VARCHAR}
|
||||
,#{sectionsCode,jdbcType=VARCHAR}, #{labelEnum,jdbcType=VARCHAR}, #{signalCode,jdbcType=VARCHAR}
|
||||
,#{runModel,jdbcType=VARCHAR},#{runStatus,jdbcType=VARCHAR},#{relativeCode,jdbcType=VARCHAR}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<insert id="insertList">
|
||||
insert into draft_map_station_direction_label (
|
||||
map_id, code, station_code, sections_code, label_enum, signal_code
|
||||
map_id, code, station_code, sections_code, label_enum
|
||||
,signal_code, run_model, run_status, relative_code
|
||||
)
|
||||
values
|
||||
<foreach collection="list" item="record" separator=",">
|
||||
(
|
||||
#{record.mapId,jdbcType=BIGINT}, #{record.code,jdbcType=VARCHAR}, #{record.stationCode,jdbcType=VARCHAR},
|
||||
#{record.sectionsCode,jdbcType=VARCHAR}, #{record.labelEnum,jdbcType=VARCHAR},
|
||||
#{record.signalCode,jdbcType=VARCHAR}
|
||||
#{record.signalCode,jdbcType=VARCHAR}, #{record.runModel,jdbcType=VARCHAR},
|
||||
#{record.runStatus,jdbcType=VARCHAR},#{record.relativeCode,jdbcType=VARCHAR}
|
||||
)
|
||||
</foreach>
|
||||
|
||||
@ -158,6 +165,9 @@
|
||||
<if test="sectionsCode != null">sections_code,</if>
|
||||
<if test="labelEnum != null">label_enum,</if>
|
||||
<if test="signalCode != null">signal_code,</if>
|
||||
<if test="runModel != null">run_model,</if>
|
||||
<if test="runStatus != null">run_status,</if>
|
||||
<if test="relativeCode != null">relative_code,</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="mapId != null">#{mapId,jdbcType=BIGINT},</if>
|
||||
@ -166,6 +176,9 @@
|
||||
<if test="sectionsCode != null">#{sectionsCode,jdbcType=VARCHAR},</if>
|
||||
<if test="labelEnum != null">#{labelEnum,jdbcType=VARCHAR},</if>
|
||||
<if test="signalCode != null">#{signalCode,jdbcType=VARCHAR},</if>
|
||||
<if test="runModel != null">#{runModel,jdbcType=VARCHAR},</if>
|
||||
<if test="runStatus != null">#{runStatus,jdbcType=VARCHAR},</if>
|
||||
<if test="relativeCode != null">#{relativeCode,jdbcType=VARCHAR},</if>
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
@ -201,6 +214,15 @@
|
||||
<if test="record.signalCode != null">
|
||||
signal_code = #{record.signalCode,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.runModel != null">
|
||||
run_model = #{record.runModel,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.defaultRunStatus != null">
|
||||
run_status = #{record.runStatus,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.relativeCode != null">
|
||||
relative_code = #{record.relativeCode,jdbcType=VARCHAR},
|
||||
</if>
|
||||
</set>
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause"/>
|
||||
@ -216,6 +238,9 @@
|
||||
sections_code = #{record.sectionsCode,jdbcType=VARCHAR},
|
||||
label_enum = #{record.labelEnum,jdbcType=VARCHAR},
|
||||
signal_code = #{record.signalCode,jdbcType=VARCHAR},
|
||||
run_model = #{record.runModel,jdbcType=VARCHAR},
|
||||
run_status = #{record.runStatus,jdbcType=VARCHAR},
|
||||
relative_code = #{record.relativeCode,jdbcType=VARCHAR},
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause"/>
|
||||
</if>
|
||||
@ -242,6 +267,16 @@
|
||||
<if test="signalCode != null">
|
||||
signal_code = #{signalCode,jdbcType=VARCHAR},
|
||||
</if>
|
||||
|
||||
<if test="record.runModel != null">
|
||||
run_model = #{runModel,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="defaultRunStatus != null">
|
||||
run_status = #{runStatus,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="relativeCode != null">
|
||||
relative_code = #{relativeCode,jdbcType=VARCHAR},
|
||||
</if>
|
||||
</set>
|
||||
where id = #{id,jdbcType=BIGINT}
|
||||
</update>
|
||||
@ -255,6 +290,9 @@
|
||||
sections_code = #{sectionsCode,jdbcType=VARCHAR},
|
||||
label_enum = #{labelEnum,jdbcType=VARCHAR},
|
||||
signal_code = #{signalCode,jdbcType=VARCHAR},
|
||||
run_model = #{runModel,jdbcType=VARCHAR},
|
||||
run_status = #{runStatus,jdbcType=VARCHAR},
|
||||
relative_code = #{relativeCode,jdbcType=VARCHAR},
|
||||
where id = #{id,jdbcType=BIGINT}
|
||||
</update>
|
||||
</mapper>
|
Loading…
Reference in New Issue
Block a user