From 126f283d0338848276079b5bbd2c5a3f143a4894 Mon Sep 17 00:00:00 2001 From: liurunyu <lry9898@163.com> Date: 星期二, 12 十一月 2024 14:50:45 +0800 Subject: [PATCH] Merge branch 'master' of http://8.140.179.55:20000/r/pipIrr-SV --- pipIrr-platform/pipIrr-global/src/main/resources/mapper/SeVirtualCardMapper.xml | 17 ++++- pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/command/ComSupport.java | 23 ++++++- pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/pojoSe/SeVirtualCard.java | 17 ++++- pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/voRm/VoUnclosedValve.java | 14 ++++ pipIrr-platform/pipIrr-web/pipIrr-web-remote/src/main/java/com/dy/pipIrrRemote/valve/ValveCtrl.java | 24 +++---- pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/command/ValveCtrl.java | 20 +++--- pipIrr-platform/pipIrr-global/src/main/resources/mapper/RmCommandHistoryMapper.xml | 12 +++ pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/daoRm/RmCommandHistoryMapper.java | 7 ++ 8 files changed, 94 insertions(+), 40 deletions(-) diff --git a/pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/command/ComSupport.java b/pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/command/ComSupport.java index 35c8b85..db4830b 100644 --- a/pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/command/ComSupport.java +++ b/pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/command/ComSupport.java @@ -30,12 +30,11 @@ import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.Random; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -362,6 +361,7 @@ if (comType == 1) { virtualCard.setInUse((byte) 0); virtualCard.setIntakeId(null); + virtualCard.setOpenTime(null); } else { virtualCard.setInUse((byte) 1); virtualCard.setIntakeId(intakeId); @@ -384,6 +384,21 @@ if (comType == 1) { virtualCard.setInUse((byte) 1); virtualCard.setIntakeId(intakeId); + + // 濡傛灉鏄鍒掑紑闃�锛屼粠鍛戒护鏃ュ織涓幏鍙栬鍒掓椂闂达紝鍚﹀垯鍙栧綋鍓嶆椂闂� + if(commandCode.equals(CodeV1.cd_A1) || commandCode.equals(CodeV1.cd_A2)) { + // 璁″垝寮�闃� + Date openTime = null; + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + try { + openTime = sdf.parse(rmCommandHistoryMapper.getTimeByCommId(comId)); + }catch (ParseException e) { + } + virtualCard.setOpenTime(openTime); + }else { + // 闈炶鍒掑紑闃� + virtualCard.setOpenTime(new Date()); + } } else { virtualCard.setInUse((byte) 0); virtualCard.setIntakeId(null); diff --git a/pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/daoRm/RmCommandHistoryMapper.java b/pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/daoRm/RmCommandHistoryMapper.java index c5d59e7..20e64a6 100644 --- a/pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/daoRm/RmCommandHistoryMapper.java +++ b/pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/daoRm/RmCommandHistoryMapper.java @@ -60,4 +60,11 @@ * @return */ List<VoCommand> getCommandHistories(Map<?, ?> params); + + /** + * 鏍规嵁鍛戒护鏃ュ織ID鑾峰彇棰勭害鏃堕棿锛屽悜铏氭嫙鍗″啓寮�闃�鏃堕棿鐢� + * @param commId + * @return + */ + String getTimeByCommId(Long commId); } \ No newline at end of file diff --git a/pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/pojoSe/SeVirtualCard.java b/pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/pojoSe/SeVirtualCard.java index 7bf06e6..752b6c7 100644 --- a/pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/pojoSe/SeVirtualCard.java +++ b/pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/pojoSe/SeVirtualCard.java @@ -93,15 +93,22 @@ private Byte inUse; /** + * 鍙栨按鍙D(铏氭嫙鍗′娇鐢ㄦ椂鎵�搴旂敤浜庣殑鍙栨按鍙D) + */ + @Schema(description = "鍙栨按鍙D", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private Long intakeId; + + /** + * 寮�闃�鏃堕棿 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date openTime; + + /** * 鍒涘缓鏃堕棿 */ @Schema(description = "鍒涘缓鏃堕棿", requiredMode = Schema.RequiredMode.NOT_REQUIRED) private Date createTime; - /** - * 鍙栨按鍙D(铏氭嫙鍗′娇鐢ㄦ椂鎵�搴旂敤浜庣殑鍙栨按鍙D) - */ - @Schema(description = "鍙栨按鍙D", requiredMode = Schema.RequiredMode.NOT_REQUIRED) - private Long intakeId; } \ No newline at end of file diff --git a/pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/voRm/VoUnclosedValve.java b/pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/voRm/VoUnclosedValve.java index c556bf8..beca40c 100644 --- a/pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/voRm/VoUnclosedValve.java +++ b/pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/voRm/VoUnclosedValve.java @@ -4,6 +4,8 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder; import lombok.Data; +import java.util.Date; + /** * @author ZhuBaoMin * @date 2024-05-24 16:30 @@ -12,7 +14,7 @@ */ @Data -@JsonPropertyOrder({"intakeNum", "isOnLine", "rtuAddr", "vcNum", "orderNo", "state", "planned"}) +@JsonPropertyOrder({"intakeNum", "isOnLine", "rtuAddr", "vcNum", "orderNo", "state", "planned", "dt", "waterInstant"}) public class VoUnclosedValve implements BaseEntity { private static final long serialVersionUID = 202405241634001L; @@ -53,5 +55,13 @@ */ private Boolean planned; - //private Date openTime; + /** + * 宸ヤ綔鎶ユ椂闂� + */ + private Date dt; + + /** + * 鐬椂娴侀噺 + */ + private Double waterInstant; } diff --git a/pipIrr-platform/pipIrr-global/src/main/resources/mapper/RmCommandHistoryMapper.xml b/pipIrr-platform/pipIrr-global/src/main/resources/mapper/RmCommandHistoryMapper.xml index 015e496..4e36ac2 100644 --- a/pipIrr-platform/pipIrr-global/src/main/resources/mapper/RmCommandHistoryMapper.xml +++ b/pipIrr-platform/pipIrr-global/src/main/resources/mapper/RmCommandHistoryMapper.xml @@ -199,7 +199,7 @@ <select id="getUnclosedValves" resultType="com.dy.pipIrrGlobal.voRm.VoUnclosedValve"> SELECT inta.name AS intakeNum, - IFNULL(rtus.isOnLine,'鏈煡') AS isOnLine, + IFNULL(rtus.isOnLine, false) AS isOnLine, com.rtu_addr AS rtuAddr, com.param ->> '$.icCardNo' AS vcNum, ( @@ -211,7 +211,7 @@ ) AS orderNo, 'toClose' AS state, CASE - WHEN com.command_code = 'A1' OR com.command_code = 'A2' THEN 0 + WHEN com.command_code = 'A1' OR com.command_code = 'A2' THEN 1 ELSE 0 END AS planned, com.send_time AS sendTime, @@ -352,4 +352,12 @@ </if> </trim> </select> + + <!--鏍规嵁鍛戒护鏃ュ織ID鑾峰彇棰勭害鏃堕棿锛屽悜铏氭嫙鍗″啓寮�闃�鏃堕棿鐢�--> + <select id="getTimeByCommId" resultType="java.lang.String"> + SELECT + CONCAT(param ->> '$.year', '-', param ->> '$.month', '-', param ->> '$.day', ' ', param ->> '$.hour', ':', param ->> '$.minute', ':00') AS openTime + FROM rm_command_history + WHERE com_id = #{commId} + </select> </mapper> \ No newline at end of file diff --git a/pipIrr-platform/pipIrr-global/src/main/resources/mapper/SeVirtualCardMapper.xml b/pipIrr-platform/pipIrr-global/src/main/resources/mapper/SeVirtualCardMapper.xml index 0331a56..dfa7059 100644 --- a/pipIrr-platform/pipIrr-global/src/main/resources/mapper/SeVirtualCardMapper.xml +++ b/pipIrr-platform/pipIrr-global/src/main/resources/mapper/SeVirtualCardMapper.xml @@ -13,11 +13,12 @@ <result column="last_operate_time" jdbcType="TIMESTAMP" property="lastOperateTime" /> <result column="in_use" jdbcType="TINYINT" property="inUse" /> <result column="intake_id" jdbcType="BIGINT" property="intakeId" /> + <result column="open_time" jdbcType="TIMESTAMP" property="openTime" /> <result column="create_time" jdbcType="TIMESTAMP" property="createTime" /> </resultMap> <sql id="Base_Column_List"> <!--@mbg.generated--> - id, vc_num, client_id, money, state, last_operate, last_operate_time, in_use, intake_id, create_time + id, vc_num, client_id, money, state, last_operate, last_operate_time, in_use, intake_id, open_time, create_time </sql> <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap"> <!--@mbg.generated--> @@ -36,10 +37,10 @@ <!--@mbg.generated--> insert into se_virtual_card (id, vc_num, client_id, money, state, last_operate, last_operate_time, - in_use, intake_id, create_time) + in_use, intake_id, open_time, create_time) values (#{id,jdbcType=BIGINT}, #{vcNum,jdbcType=BIGINT}, #{clientId,jdbcType=BIGINT}, #{money,jdbcType=FLOAT}, #{state,jdbcType=TINYINT}, #{lastOperate,jdbcType=TINYINT}, #{lastOperateTime,jdbcType=TIMESTAMP}, - #{inUse,jdbcType=TINYINT}, #{intakeId,jdbcType=BIGINT},#{createTime,jdbcType=TIMESTAMP}) + #{inUse,jdbcType=TINYINT}, #{intakeId,jdbcType=BIGINT},#{openTime,jdbcType=TIMESTAMP},#{createTime,jdbcType=TIMESTAMP}) </insert> <insert id="insertSelective" parameterType="com.dy.pipIrrGlobal.pojoSe.SeVirtualCard"> <!--@mbg.generated--> @@ -72,6 +73,9 @@ <if test="intakeId != null"> intake_id, </if> + <if test="openTime != null"> + open_time, + </if> <if test="createTime != null"> create_time, </if> @@ -103,6 +107,9 @@ </if> <if test="intakeId != null"> #{intakeId,jdbcType=BIGINT}, + </if> + <if test="openTime != null"> + #{openTime,jdbcType=TIMESTAMP}, </if> <if test="createTime != null"> #{createTime,jdbcType=TIMESTAMP}, @@ -137,6 +144,9 @@ <if test="intakeId != null"> intake_id = #{intakeId,jdbcType=BIGINT}, </if> + <if test="openTime != null"> + open_time = #{openTime,jdbcType=TIMESTAMP}, + </if> <if test="createTime != null"> create_time = #{createTime,jdbcType=TIMESTAMP}, </if> @@ -154,6 +164,7 @@ last_operate_time = #{lastOperateTime,jdbcType=TIMESTAMP}, in_use = #{inUse,jdbcType=TINYINT}, intake_id = #{intakeId,jdbcType=BIGINT}, + open_time = #{openTime,jdbcType=TIMESTAMP}, create_time = #{createTime,jdbcType=TIMESTAMP} where id = #{id,jdbcType=BIGINT} </update> diff --git a/pipIrr-platform/pipIrr-web/pipIrr-web-remote/src/main/java/com/dy/pipIrrRemote/valve/ValveCtrl.java b/pipIrr-platform/pipIrr-web/pipIrr-web-remote/src/main/java/com/dy/pipIrrRemote/valve/ValveCtrl.java index 53778ea..9823cfe 100644 --- a/pipIrr-platform/pipIrr-web/pipIrr-web-remote/src/main/java/com/dy/pipIrrRemote/valve/ValveCtrl.java +++ b/pipIrr-platform/pipIrr-web/pipIrr-web-remote/src/main/java/com/dy/pipIrrRemote/valve/ValveCtrl.java @@ -60,7 +60,6 @@ public class ValveCtrl extends ComSupport { private final CommandSv commandSv; private final SeVirtualCardMapper seVirtualCardMapper; - private final IDLongGenerator idLongGenerator; @Value("${mw.rtuCallbackUrl_rm}") private String rtuCallbackUrl_rm; @@ -75,10 +74,9 @@ private String key_mw = "comSendUrl"; @Autowired - public ValveCtrl(CommandSv commandSv, SeVirtualCardMapper seVirtualCardMapper, IDLongGenerator idLongGenerator, Environment env) { + public ValveCtrl(CommandSv commandSv, SeVirtualCardMapper seVirtualCardMapper, Environment env) { this.commandSv = commandSv; this.seVirtualCardMapper = seVirtualCardMapper; - this.idLongGenerator = idLongGenerator; this.env = env; } @@ -100,7 +98,7 @@ Long intakeId = valve.getIntakeId(); Long vcId = valve.getVcId(); Long operator = valve.getOperator(); - Long comId = idLongGenerator.generate(); + Long comId = new IDLongGenerator().generate(); // 鑾峰彇姘翠环 Double waterPrice = commandSv.getPrice(); @@ -238,7 +236,7 @@ String vcNum = voUnclosedParam.getVcNum(); Long vcId = Optional.ofNullable(seVirtualCardMapper.getVcIdByNum(vcNum)).orElse(0L); String orderNo = voUnclosedParam.getOrderNo(); - Long comId = idLongGenerator.generate(); + Long comId = new IDLongGenerator().generate(); // 闃�鎺у櫒鍦板潃鎹㈠彇姘村彛ID鍜岄�氳鍗忚 JSONObject job_rtu = getRtu(null, rtuAddr); @@ -328,7 +326,7 @@ Long intakeId = valve.getIntakeId(); Long vcId = valve.getVcId(); Long operator = valve.getOperator(); - Long comId = idLongGenerator.generate(); + Long comId = new IDLongGenerator().generate(); /** * 濡傛灉鍐滄埛閫夋嫨浜嗚櫄鎷熷崱锛屽垯浣跨敤璇ヨ櫄鎷熷崱 @@ -479,7 +477,7 @@ String vcNum = voUnclosedParam.getVcNum(); Long vcId = Optional.ofNullable(seVirtualCardMapper.getVcIdByNum(vcNum)).orElse(0L); String orderNo = voUnclosedParam.getOrderNo(); - Long comId = idLongGenerator.generate(); + Long comId = new IDLongGenerator().generate(); // 闃�鎺у櫒鍦板潃鎹㈠彇姘村彛ID鍜岄�氳鍗忚 JSONObject job_rtu = getRtu(null, rtuAddr); @@ -574,7 +572,7 @@ Long vcId = automaticClose.getVcId(); Integer minutes = automaticClose.getMinutes(); Long operator = automaticClose.getOperator(); - Long comId = idLongGenerator.generate(); + Long comId = new IDLongGenerator().generate(); /** * 濡傛灉鍐滄埛閫夋嫨浜嗚櫄鎷熷崱锛屽垯浣跨敤璇ヨ櫄鎷熷崱 @@ -678,7 +676,7 @@ Long vcId = automaticClose.getVcId(); Integer waterAmount = automaticClose.getWaterAmount(); Long operator = automaticClose.getOperator(); - Long comId = idLongGenerator.generate(); + Long comId = new IDLongGenerator().generate(); /** * 濡傛灉鍐滄埛閫夋嫨浜嗚櫄鎷熷崱锛屽垯浣跨敤璇ヨ櫄鎷熷崱 @@ -783,7 +781,7 @@ Date plannedOpenTime = automaticClose.getPlannedOpenTime(); Integer minutes = automaticClose.getMinutes(); Long operator = automaticClose.getOperator(); - Long comId = idLongGenerator.generate(); + Long comId = new IDLongGenerator().generate(); Integer year = Integer.parseInt(String.format("%tY", plannedOpenTime)); Integer month = Integer.parseInt(String.format("%tm", plannedOpenTime)); @@ -899,7 +897,7 @@ Date plannedOpenTime = automaticClose.getPlannedOpenTime(); Integer waterAmount = automaticClose.getWaterAmount(); Long operator = automaticClose.getOperator(); - Long comId = idLongGenerator.generate(); + Long comId = new IDLongGenerator().generate(); Integer year = Integer.parseInt(String.format("%tY", plannedOpenTime)); Integer month = Integer.parseInt(String.format("%tm", plannedOpenTime)); @@ -1051,7 +1049,7 @@ Double chargeWater = po.getChargeWater(); Long operator = po.getOperator(); String flowNo = RandomStringUtils.randomNumeric(12); // 鐢熸垚12浣嶉殢鏈烘暟 - Long comId = idLongGenerator.generate(); + Long comId = new IDLongGenerator().generate(); // 鍙栨按鍙D鎹㈤榾鎺у櫒鍦板潃鍙婇�氳鍗忚 JSONObject job_rtu = getRtu(intakeId, null); @@ -1124,7 +1122,7 @@ Long intakeId = card.getIntakeId(); String cardAddr = card.getCardAddr(); Long operator = card.getOperator(); - Long comId = idLongGenerator.generate(); + Long comId = new IDLongGenerator().generate(); // 鍙栨按鍙D鎹㈤榾鎺у櫒鍦板潃鍙婇�氳鍗忚 JSONObject job_rtu = getRtu(intakeId, null); diff --git a/pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/command/ValveCtrl.java b/pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/command/ValveCtrl.java index db5cd4d..4d03330 100644 --- a/pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/command/ValveCtrl.java +++ b/pipIrr-platform/pipIrr-web/pipIrr-web-wechat/src/main/java/com/dy/pipIrrWechat/command/ValveCtrl.java @@ -55,7 +55,6 @@ public class ValveCtrl extends ComSupport { private final CommandSv commandSv; private final SeVirtualCardMapper seVirtualCardMapper; - private final IDLongGenerator idLongGenerator; @Value("${mw.rtuCallbackUrl_wx}") private String rtuCallbackUrl_wx; @@ -70,10 +69,9 @@ private String key_mw = "comSendUrl"; @Autowired - public ValveCtrl(CommandSv commandSv, SeVirtualCardMapper seVirtualCardMapper, IDLongGenerator idLongGenerator, Environment env) { + public ValveCtrl(CommandSv commandSv, SeVirtualCardMapper seVirtualCardMapper, Environment env) { this.commandSv = commandSv; this.seVirtualCardMapper = seVirtualCardMapper; - this.idLongGenerator = idLongGenerator; this.env = env; } @@ -84,7 +82,7 @@ * @return */ @PostMapping(path = "open_wx", consumes = MediaType.APPLICATION_JSON_VALUE) - //@Transactional(rollbackFor = Exception.class) + @Transactional(rollbackFor = Exception.class) public BaseResponse<Boolean> open(@RequestBody @Valid ValveOpen valve, BindingResult bindingResult) { if (bindingResult != null && bindingResult.hasErrors()) { return BaseResponseUtils.buildErrorMsg(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage()); @@ -95,7 +93,7 @@ Long vcId = valve.getVcId(); Boolean forceOpen = valve.getForceOpen(); Long operator = valve.getOperator(); - Long comId = idLongGenerator.generate(); + Long comId = new IDLongGenerator().generate(); if(intakeId == null && intakeName == null) { return BaseResponseUtils.buildErrorMsg(WechatResultCode.PLEASE_SELECT_A_INTAKE.getMessage()); @@ -246,7 +244,7 @@ Long vcId = Optional.ofNullable(seVirtualCardMapper.getVcIdByNum(vcNum)).orElse(0L); String orderNo = valve.getOrderNo(); Long operator = valve.getOperator(); - Long comId = idLongGenerator.generate(); + Long comId = new IDLongGenerator().generate(); // 闃�鎺у櫒鍦板潃鎹㈠彇姘村彛ID鍜岄�氳鍗忚 JSONObject job_rtu = getRtu(null, rtuAddr); @@ -341,7 +339,7 @@ Long vcId = automaticClose.getVcId(); Integer minutes = automaticClose.getMinutes(); Long operator = automaticClose.getOperator(); - Long comId = idLongGenerator.generate(); + Long comId = new IDLongGenerator().generate(); /** * 濡傛灉鍐滄埛閫夋嫨浜嗚櫄鎷熷崱锛屽垯浣跨敤璇ヨ櫄鎷熷崱 @@ -444,7 +442,7 @@ Long vcId = automaticClose.getVcId(); Integer waterAmount = automaticClose.getWaterAmount(); Long operator = automaticClose.getOperator(); - Long comId = idLongGenerator.generate(); + Long comId = new IDLongGenerator().generate(); /** * 濡傛灉鍐滄埛閫夋嫨浜嗚櫄鎷熷崱锛屽垯浣跨敤璇ヨ櫄鎷熷崱 @@ -548,7 +546,7 @@ Date plannedOpenTime = automaticClose.getPlannedOpenTime(); Integer minutes = automaticClose.getMinutes(); Long operator = automaticClose.getOperator(); - Long comId = idLongGenerator.generate(); + Long comId = new IDLongGenerator().generate(); Integer year = Integer.parseInt(String.format("%tY", plannedOpenTime)); Integer month = Integer.parseInt(String.format("%tm", plannedOpenTime)); @@ -663,7 +661,7 @@ Date plannedOpenTime = automaticClose.getPlannedOpenTime(); Integer waterAmount = automaticClose.getWaterAmount(); Long operator = automaticClose.getOperator(); - Long comId = idLongGenerator.generate(); + Long comId = new IDLongGenerator().generate(); Integer year = Integer.parseInt(String.format("%tY", plannedOpenTime)); Integer month = Integer.parseInt(String.format("%tm", plannedOpenTime)); @@ -812,7 +810,7 @@ Double chargeWater = po.getChargeWater(); Long operator = po.getOperator(); String flowNo = RandomStringUtils.randomNumeric(12); // 鐢熸垚12浣嶉殢鏈烘暟 - Long comId = idLongGenerator.generate(); + Long comId = new IDLongGenerator().generate(); // 鍙栨按鍙D鎹㈤榾鎺у櫒鍦板潃鍙婇�氳鍗忚 JSONObject job_rtu = getRtu(intakeId, null); -- Gitblit v1.8.0