From 62c24bfb0f54998fceeb3b37eb14a74e6bfd83e9 Mon Sep 17 00:00:00 2001 From: guonan <guonan201020@163.com> Date: 星期二, 01 七月 2025 11:01:35 +0800 Subject: [PATCH] 修改小时雨强 echart暂停播放的问题 威胁人房户 定时器问题 --- src/components/monifangzhen/echartInfo.vue | 46 ++++- src/components/tools/DebuffDetail.vue | 231 ++++++++++++++++------------ src/views/left/CitySim.vue | 167 +++++++++++++------- src/components/monifangzhen/schemeCard.vue | 5 4 files changed, 281 insertions(+), 168 deletions(-) diff --git a/src/components/monifangzhen/echartInfo.vue b/src/components/monifangzhen/echartInfo.vue index 3f73aaa..c2e4352 100644 --- a/src/components/monifangzhen/echartInfo.vue +++ b/src/components/monifangzhen/echartInfo.vue @@ -450,16 +450,33 @@ } }; - // 鎺у埗鏂规硶锛氱簿纭帶鍒跺姩鐢绘椂闂达紝鏈�鍚庝竴甯у湪绗� 90 绉� + let fixedFrameNum = null; + let startTime = null; // 灏唖tartTime绉诲埌澶栧眰 + let elapsedBeforePause = 0; // 璁板綍鏆傚仠鍓嶅凡缁忚繃鍘荤殑鏃堕棿 + const startUpdating = () => { - if (updateInterval || dataIndex.value >= rainfallData.value.length) return; + if (updateInterval || dataIndex.value >= rainfallData.value.length) { + console.log("Animation already running or completed"); + return; + } - const totalDuration = simStore.frameNum * 1000; // 90绉� + // 濡傛灉鏄娆″惎鍔ㄦ垨閲嶆柊寮�濮� + if (fixedFrameNum === null) { + fixedFrameNum = simStore.frameNum; + elapsedBeforePause = 0; + startTime = Date.now(); + } else { + // 濡傛灉鏄殏鍋滃悗缁х画锛岃皟鏁磗tartTime浠ュ弽鏄犲凡缁忚繃鍘荤殑鏃堕棿 + startTime = Date.now() - elapsedBeforePause; + } + + const totalDuration = fixedFrameNum * 1000; const totalPoints = rainfallData.value.length; - const startTime = Date.now(); - const animate = (index = 0) => { + + const animate = (index) => { if (index >= totalPoints) { + console.log("Animation completed"); stopUpdating(); return; } @@ -469,7 +486,7 @@ const delay = Math.max(0, startTime + expectedTime - now); updateInterval = setTimeout(() => { - dataIndex.value = index + 1; // 鍥犱负鏄粠 0 寮�濮� push 鐨� + dataIndex.value = index; updateData(); animate(index + 1); }, delay); @@ -478,13 +495,26 @@ animate(dataIndex.value); }; + // 鏆傚仠鍑芥暟闇�瑕佽褰曞凡缁忚繃鍘荤殑鏃堕棿 const stopUpdating = () => { - clearTimeout(updateInterval); - updateInterval = null; + if (updateInterval) { + clearTimeout(updateInterval); + updateInterval = null; + // 璁板綍鏆傚仠鏃跺凡缁忚繃鍘荤殑鏃堕棿 + elapsedBeforePause = Date.now() - startTime; + } }; + + // const stopUpdating = () => { + // clearTimeout(updateInterval); + // updateInterval = null; + // }; const resetLoading = () => { stopUpdating(); + fixedFrameNum = null; + startTime = null; + elapsedBeforePause = 0; dataIndex.value = 0; data1.value = [0]; data2.value = [0]; diff --git a/src/components/monifangzhen/schemeCard.vue b/src/components/monifangzhen/schemeCard.vue index 32073b0..ec72b32 100644 --- a/src/components/monifangzhen/schemeCard.vue +++ b/src/components/monifangzhen/schemeCard.vue @@ -220,9 +220,11 @@ (item) => item.result == "鍒涘缓浠跨湡" || item.result == "瀹屾垚" || - item.result == "-1" + item.result == "-1" || + item.result == null ); simAPIStore.shouldPoll = !shouldStop; // 淇敼 Pinia 鐘舵�� + console.log(shouldStop, "aaaaaaaaaaaaaaaa"); // 3. 濡傛灉闇�瑕佸仠姝� if (shouldStop) { if (intervalId) { @@ -241,7 +243,6 @@ watch( () => simAPIStore.shouldPoll, (isStarted) => { - console.log(isStarted, "瀹氭椂鍣�"); if (isStarted) { getScheme(); // 棣栨绔嬪嵆鑾峰彇涓�娆� intervalId = setInterval(getScheme, 60 * 1000); // 姣忛殧涓�鍒嗛挓鎵ц diff --git a/src/components/tools/DebuffDetail.vue b/src/components/tools/DebuffDetail.vue index f5b1506..1c4b688 100644 --- a/src/components/tools/DebuffDetail.vue +++ b/src/components/tools/DebuffDetail.vue @@ -12,108 +12,149 @@ </div> </template> -<script> -// 鐘舵�佺鐞嗗櫒 +<script setup> +import { ref, onMounted } from "vue"; import { useSimStore } from "@/store/simulation"; import { storeToRefs } from "pinia"; + const simStore = useSimStore(); const { selectedScheme } = storeToRefs(simStore); -export default { - name: "detail", - components: {}, - props: { - areaName: { - type: String, - default: "灏瑰瑗挎矡", - }, - }, - data() { - return { - show: false, - detailList: [ - { - name: "鏈�澶ч洦寮猴細", - value: Number(Math.random() * 100).toFixed(2) + " mm/h", - }, - { - name: "骞冲潎闆ㄥ己锛�", - value: Number(Math.random() * 10).toFixed(2) + " mm/h", - }, - { - name: "鏈�澶ф按娣憋細", - value: "1.86 m", - }, - { - name: "鏈�澶ф祦閫燂細", - value: "7 m/s", - }, - { - name: "濞佽儊鎴垮眿锛�", - value: "406 闂�", - }, - { - name: "濞佽儊浜哄彛锛�", - value: "145 鎴�", - }, - { - name: "濞佽儊璐骇锛�", - value: "4872 涓囧厓", - }, - ], - }; - }, - mounted() { - this.getRainfallData(); // 缁勪欢鎸傝浇鍚庢墽琛岃幏鍙栭洦閲忔暟鎹� - }, - methods: { - getRainfallData() { - if (!selectedScheme.value || !selectedScheme.value.data) { - console.warn("selectedScheme 鎴� data 涓嶅瓨鍦�"); - return; - } - let data = selectedScheme.value.data; - // 濡傛灉鏄瓧绗︿覆锛屽垯灏濊瘯瑙f瀽鎴愬璞� - if (typeof data === "string") { - try { - data = JSON.parse(data); - } catch (e) { - console.error("data 涓嶆槸鏈夋晥鐨� JSON 瀛楃涓�"); - return; - } - } +// 闅愭偅鐐� +const filteredData = simStore.DangerPoint.filter((item) => + item.position?.includes("瀛欒儭娌�") +); - if (selectedScheme.value.type !== 2) { - const rainfallList = data.rainfalls; - - // 鎻愬彇 intensity 鍊� - const rainValues = rainfallList.map((r) => r.intensity); - const minRain = Math.min(...rainValues); - const maxRain = Math.max(...rainValues); - const avgRain = - rainValues.reduce((sum, val) => sum + val, 0) / rainValues.length; - - // 鏇存柊 detailList 涓殑鈥滄渶澶ч洦寮衡�濆拰鈥滃钩鍧囬洦寮衡�� - this.detailList[0].value = maxRain.toFixed(2) + " mm/h"; // 鏈�澶ч洦寮� - this.detailList[1].value = avgRain.toFixed(2) + " mm/h"; // 骞冲潎闆ㄥ己 - - console.log( - "褰撳墠鏂规涓嬫渶灏忛洦閲忋�佹渶澶ч洦閲忋�佸钩鍧囬洦閲忥細", - minRain.toFixed(2), - maxRain.toFixed(2), - avgRain.toFixed(2) - ); - } - }, - closeMsg() { - this.$emit("close"); - }, - showMsg() { - this.$emit("open"); - }, +// 鍝嶅簲寮忔暟鎹� +const detailList = ref([ + { + name: "鏈�澶ч洦寮猴細", + value: (Math.random() * 100).toFixed(2) + " mm/h", }, + { + name: "骞冲潎闆ㄥ己锛�", + value: (Math.random() * 10).toFixed(2) + " mm/h", + }, + { + name: "鏈�澶ф按娣憋細", + value: "1.86 m", + }, + { + name: "鏈�澶ф祦閫燂細", + value: "7 m/s", + }, + { + name: "濞佽儊鎴挎暟锛�", + value: "406 闂�", + }, + { + name: "濞佽儊鎴锋暟锛�", + value: "145 鎴�", + }, + { + name: "濞佽儊浜哄彛锛�", + value: "145 浜�", + }, + { + name: "濞佽儊璐骇锛�", + value: "4872 涓囧厓", + }, +]); + +const updateThreatData = () => { + // 绛涢�� position 鍖呭惈 "瀛欒儭娌�" 鐨勬暟鎹� + const filteredData = simStore.DangerPoint.filter((item) => + item.position?.includes("瀛欒儭娌�") + ); + + if (filteredData.length === 0) { + console.warn("鏈壘鍒� position 鍖呭惈 '瀛欒儭娌�' 鐨勬暟鎹�"); + return; + } + + // 鍒濆鍖栫疮鍔犲�� + let totalHousehold = 0; // 濞佽儊鎴锋暟 + let totalPerson = 0; // 濞佽儊浜烘暟 + let totalRoom = 0; // 濞佽儊鎴挎暟 + + // 閬嶅巻骞剁疮鍔� + filteredData.forEach((item) => { + totalHousehold += Number(item.threatHouseNum) || 0; + totalPerson += Number(item.threatPersonNum) || 0; + totalRoom += Number(item.threatRoomNum) || 0; + }); + + // 鏇存柊 detailList + detailList.value[5].value = `${totalHousehold} 鎴穈; + detailList.value[6].value = `${totalPerson} 浜篳; + detailList.value[4].value = `${totalRoom} 闂碻; + + console.log("濞佽儊鎴锋暟:", totalHousehold); + console.log("濞佽儊浜烘暟:", totalPerson); + console.log("濞佽儊鎴挎暟:", totalRoom); }; +// 鏂规硶瀹氫箟 +const getRainfallData = () => { + if (!selectedScheme.value || !selectedScheme.value.data) { + console.warn("selectedScheme 鎴� data 涓嶅瓨鍦�"); + return; + } + + let data = selectedScheme.value.data; + + // 濡傛灉鏄瓧绗︿覆锛屽垯灏濊瘯瑙f瀽鎴愬璞� + if (typeof data === "string") { + try { + data = JSON.parse(data); + } catch (e) { + console.error("data 涓嶆槸鏈夋晥鐨� JSON 瀛楃涓�"); + return; + } + } + + if (selectedScheme.value.type !== 2) { + const rainfallList = data.rainfalls; + + // 鎻愬彇 intensity 鍊� + const rainValues = rainfallList.map((r) => r.intensity); + const minRain = Math.min(...rainValues); + const maxRain = Math.max(...rainValues); + const avgRain = + rainValues.reduce((sum, val) => sum + val, 0) / rainValues.length; + + // 鏇存柊 detailList 涓殑鈥滄渶澶ч洦寮衡�濆拰鈥滃钩鍧囬洦寮衡�� + detailList.value[0].value = maxRain.toFixed(2) + " mm/h"; // 鏈�澶ч洦寮� + detailList.value[1].value = avgRain.toFixed(2) + " mm/h"; // 骞冲潎闆ㄥ己 + + console.log( + "褰撳墠鏂规涓嬫渶灏忛洦閲忋�佹渶澶ч洦閲忋�佸钩鍧囬洦閲忥細", + minRain.toFixed(2), + maxRain.toFixed(2), + avgRain.toFixed(2) + ); + } +}; + +const closeMsg = () => { + // 浣跨敤 defineEmits 瀹氫箟 emit + emit("close"); +}; + +const showMsg = () => { + emit("open"); +}; + +// 瀹氫箟 emit +const emit = defineEmits(["close", "open"]); + +// 鐢熷懡鍛ㄦ湡閽╁瓙 +onMounted(() => { + console.log(filteredData); + updateThreatData(); + getRainfallData(); +}); </script> + <style lang="less" scoped> .detail { background: url("@/assets/img/tools/messagebg.png"); @@ -133,7 +174,6 @@ left: 20px; font-weight: 700; font-size: 18px; - font-weight: 700; color: #fff; line-height: 40px; width: 270px; @@ -148,11 +188,8 @@ height: 20px; text-align: center; line-height: 20px; - text-align: center; - font-weight: 700; font-size: 18px; - font-weight: 700; color: #fff; cursor: pointer; } diff --git a/src/views/left/CitySim.vue b/src/views/left/CitySim.vue index 27af36b..63348ca 100644 --- a/src/views/left/CitySim.vue +++ b/src/views/left/CitySim.vue @@ -447,15 +447,16 @@ /** * 鏁版嵁澶勭悊涓诲嚱鏁� + * @param {Array} data - 瑙f瀽鍚庣殑鍘熷鏁版嵁鏁扮粍锛屾瘡涓厓绱犳槸涓�涓璞� */ const processData = (data) => { - // 妫�鏌ョ┖鏁版嵁 + // 妫�鏌ユ槸鍚︿负绌烘暟鎹� if (data.length === 0) { ElMessage.warning("鏂囦欢鍐呭涓虹┖锛�"); return; } - // 鍖归厤瀛楁鍚� + // 鍖归厤鍒楀悕锛堜緥濡傗�滄椂闂粹�濄�佲�滃皬鏃堕洦寮衡�濓級 const columns = matchColumns(data[0]); // 鏍¢獙蹇呰瀛楁鏄惁瀛樺湪 @@ -475,22 +476,18 @@ return; } - // 鏃堕棿鍒楁牎楠屾槸鍚﹀崌搴� + // 鏍¢獙鏃堕棿鍒楁槸鍚﹀崌搴忔帓鍒� if (!isTimeColumnSorted(data, columns.time)) { ElMessage.error("鏃堕棿鍒楀繀椤绘寜鍗囧簭鎺掑垪锛�"); forms.fileList = []; return; } - // 鎻愬彇鍗曚綅 - forms.intensityUnit = extractUnitFromHeader(columns.intensity); + // 鎻愬彇鍗曚綅锛堝 mm/h锛夛紝鑻ユ病鏈夊垯璁句负绌哄瓧绗︿覆 + forms.intensityUnit = extractUnitFromHeader(columns.intensity) || ""; - if (!forms.intensityUnit) { - forms.intensityUnit = ""; - } - - // 杞崲 key 鍚嶅苟杞崲鏁板�肩被鍨� - forms.rainFallList = data.map((row) => ({ + // 灏嗗師濮嬫暟鎹浆鎹负缁熶竴缁撴瀯鐨勫璞℃暟缁� + const rawRainFallList = data.map((row) => ({ time: row[columns.time], intensity: parseFloat(row[columns.intensity]), total: columns.totalRainfall @@ -498,21 +495,45 @@ : undefined, })); - console.log(forms.rainFallList, "瑙f瀽鍚庣殑闄嶉洦鏁版嵁"); + console.log(rawRainFallList, "鍘熷闄嶉洦鏁版嵁"); - // 璁$畻缁熻淇℃伅 - const firstTime = parseDateTime(data[0][columns.time]); - const lastTime = parseDateTime(data[data.length - 1][columns.time]); + // 鍒ゆ柇鏄惁涓烘暣灏忔椂鏁版嵁锛堝嵆鐩搁偦鏃堕棿闂撮殧鏄惁涓烘暣灏忔椂锛� + const isHourlyData = checkIfHourlyData(rawRainFallList); + + let hourlyRainfallList = []; + + if (!isHourlyData) { + // 濡傛灉涓嶆槸鏁村皬鏃舵暟鎹紝鎸夊皬鏃惰繘琛岃仛鍚堝鐞� + hourlyRainfallList = aggregateToHourlyRainfall(rawRainFallList); + console.log(hourlyRainfallList, "淇鍚庣殑灏忔椂闆ㄥ己"); + } else { + // 濡傛灉鏄暣灏忔椂鏁版嵁锛岀洿鎺ヤ娇鐢ㄥ師濮嬮洦寮哄�� + hourlyRainfallList = rawRainFallList.map((item) => ({ + time: item.time, + intensity: item.intensity, + })); + } + + // 鏇存柊 forms.rainFallList锛屽彲鐢ㄤ簬鍥捐〃鏄剧ず绛夌敤閫� + forms.rainFallList = hourlyRainfallList; + + // 璁$畻璧峰鏃堕棿鍜岀粨鏉熸椂闂达紙姣鏁帮級 + const firstTime = parseDateTime(hourlyRainfallList[0]?.time); + const lastTime = parseDateTime( + hourlyRainfallList[hourlyRainfallList.length - 1]?.time + ); + + // 璁$畻鎸佺画鏃堕棿锛堝崟浣嶏細灏忔椂锛� const durationSeconds = Math.floor((lastTime - firstTime) / 1000); - forms.duration = (durationSeconds / 3600).toFixed(2); // 灏忔椂 + forms.duration = (durationSeconds / 3600).toFixed(2); // 鍗曚綅锛氬皬鏃� + // 鎵惧嚭鏈�澶у皬鏃堕洦寮� const maxIntensity = Math.max( - ...data - .map((row) => parseFloat(row[columns.intensity])) - .filter((v) => !isNaN(v)) + ...hourlyRainfallList.map((item) => item.intensity).filter((v) => !isNaN(v)) ).toFixed(2); forms.intensity = maxIntensity; + // 鑻ユ湁鎬婚檷闆ㄩ噺鍒楋紝鍙栧嚭鏈�鍚庝竴涓�间綔涓烘�婚檷闆ㄩ噺 if (columns.totalRainfall) { const lastTotal = parseFloat(data[data.length - 1][columns.totalRainfall]); forms.rainfall = isNaN(lastTotal) ? 0 : lastTotal.toFixed(2); @@ -521,56 +542,80 @@ } }; -// // 澶勭悊鏁版嵁 -// const processData = (data) => { -// // 1. 妫�鏌ユ暟鎹槸鍚︿负绌� -// if (data.length === 0) { -// ElMessage.warning("鏂囦欢鍐呭涓虹┖锛�"); -// return; -// } +/** + * 妫�鏌ユ暟鎹槸鍚︿负鏁村皬鏃惰褰� + * @param {Array} rainList - 鍘熷闄嶉洦鏁版嵁鍒楄〃锛屾瘡涓厓绱犲寘鍚� time 鍜� intensity + * @returns {boolean} - 鏄惁涓烘暣灏忔椂鏁版嵁 + */ +function checkIfHourlyData(rainList) { + if (rainList.length < 2) return true; // 鍙湁涓�涓偣锛岄粯璁よ涓烘暣灏忔椂鏁版嵁 -// // 2. 鑾峰彇琛ㄥご锛堢涓�鍒楁槸鏃堕棿鍒楋級 -// const tableColumns = Object.keys(data[0]); -// const timeColumn = tableColumns[0]; // 鍋囪绗竴鍒楁槸鏃堕棿 + for (let i = 1; i < rainList.length; i++) { + // 瑙f瀽涓や釜鐩搁偦鏃堕棿鐐� + const time1 = parseDateTime(rainList[i - 1].time); + const time2 = parseDateTime(rainList[i].time); -// // 3. 鏍¢獙鏃堕棿鍒楁槸鍚︽寜鍗囧簭鎺掑垪 -// if (!isTimeColumnSorted(data, timeColumn)) { -// ElMessage.error("鏃堕棿鍒楀繀椤绘寜鍗囧簭鎺掑垪锛�"); -// forms.fileList = []; -// return; // 缁堟澶勭悊 -// } + // 璁$畻鏃堕棿宸紙鍒嗛挓锛� + const diffMinutes = Math.abs(time2 - time1) / (1000 * 60); -// const intensityColumn = tableColumns[1]; // 闆ㄥ己鍒楋紙濡� "灏忔椂闆ㄥ己(mm/h)"锛� -// // console.log(intensityColumn, "intensityColumnintensityColumnintensityColumn"); -// // 3. 鎻愬彇绗簩鍒楃殑鍗曚綅锛堝 "(mm/h)" 鈫� "mm/h"锛� -// const intensityUnit = extractUnitFromHeader(intensityColumn); -// forms.intensityUnit = intensityUnit; // 瀛樺偍鍗曚綅锛堝彲閫夛級 -// console.log(forms.intensityUnit,'aaaaaaaaaaaaaaaaaaaaa') + // 濡傛灉鏃堕棿宸笉鏄暣灏忔椂锛堜笉鑳借60鏁撮櫎锛夛紝鍒欎笉鏄暣灏忔椂鏁版嵁 + if (diffMinutes % 60 !== 0) { + return false; + } + } -// // 4. 濡傛灉鏍¢獙閫氳繃锛岀户缁鐞嗘暟鎹� -// forms.rainFallList = transformKeys(data); -// console.log(forms.rainFallList, "data"); + return true; +} -// // 5. 璁$畻闄嶉洦鏃堕暱銆侀洦寮恒�佺疮璁¢洦閲忥紙鍘熼�昏緫锛� -// const firstTime = parseDateTime(data[0][timeColumn]); -// const lastTime = parseDateTime(data[data.length - 1][timeColumn]); -// const timeDuration = Math.floor((lastTime - firstTime) / 1000); -// // 闄嶉洦鏃堕暱 -// forms.duration = (timeDuration / 3600).toFixed(2); -// // 闄嶉洦寮哄害 -// const maxValue = Math.max( -// ...data.map((row) => { -// const value = parseFloat(row[tableColumns[1]]); -// return isNaN(value) ? -Infinity : value; -// }) -// ).toFixed(2); -// forms.intensity = maxValue; +/** + * 灏嗕换鎰忔椂闂寸矑搴︾殑闆ㄥ己鏁版嵁锛屾寜灏忔椂鑱氬悎涓衡�滃皬鏃堕洦寮衡�� + * @param {Array} rainList - 鍘熷鏁版嵁鍒楄〃锛屾瘡涓厓绱犲寘鍚� time 鍜� intensity + * @returns {Array} - 鎸夊皬鏃跺垎缁勭殑鑱氬悎缁撴灉 + */ +function aggregateToHourlyRainfall(rainList) { + const grouped = {}; // 鐢ㄤ簬涓存椂瀛樺偍姣忎釜灏忔椂鐨勬暟鎹� -// const lastValue = data[data.length - 1][tableColumns[2]]; -// forms.rainfall = lastValue; + for (const item of rainList) { + // 瑙f瀽鏃堕棿瀛楃涓蹭负鏃堕棿鎴� + const timestamp = parseDateTime(item.time); -// }; + // 濡傛灉瑙f瀽澶辫触锛岃烦杩囧綋鍓嶉」 + if (isNaN(timestamp)) { + console.warn("鏃犳晥鐨勬椂闂存牸寮忥紝宸茶烦杩�", item.time); + continue; + } + // 灏嗘椂闂存埑杞负 Date 瀵硅薄浠ヤ究鎿嶄綔鏃ユ湡 + const dt = new Date(timestamp); + + // 鏋勯�犲勾鏈堟棩+灏忔椂閿紙濡傦細"2024-08-25 14"锛� + const year = String(dt.getFullYear()).padStart(4, "0"); + const month = String(dt.getMonth() + 1).padStart(2, "0"); // 娉ㄦ剰鏈堜唤浠�0寮�濮� + const date = String(dt.getDate()).padStart(2, "0"); + const hour = String(dt.getHours()).padStart(2, "0"); + + const hourKey = `${year}-${month}-${date} ${hour}`; + + // 鍒濆鍖栬灏忔椂鐨勮仛鍚堝璞� + if (!grouped[hourKey]) { + grouped[hourKey] = { + time: `${hourKey}:00:00`, // 鏍囧噯鍖栦负鏁寸偣鏃堕棿 + intensity: 0, + }; + } + + // 绱姞璇ュ皬鏃跺唴鎵�鏈夐洦寮哄�� + grouped[hourKey].intensity += item.intensity; + } + + // 灏嗚仛鍚堢粨鏋滆浆涓烘暟缁勫苟淇濈暀涓や綅灏忔暟 + const result = Object.values(grouped).map((item) => ({ + time: item.time, + intensity: Number(item.intensity.toFixed(2)), + })); + + return result; +} /** * 瑙f瀽鏃ユ湡鏃堕棿瀛楃涓叉垨Excel鏁板瓧鏃ユ湡锛岃繑鍥炴椂闂存埑锛堟绉掓暟锛� * @param {string|number} dateString - 鏃ユ湡瀛楃涓叉垨Excel鏁板瓧鏃ユ湡 -- Gitblit v1.9.3