From 79794fdb2213fb38732e838308066aa681a1f58f Mon Sep 17 00:00:00 2001 From: zhangjianrock@163.com <zhangjianrock@163.com> Date: 星期六, 13 四月 2024 13:30:45 +0800 Subject: [PATCH] 空闲时间段为空,则返回全天 --- ard-work/src/main/java/com/ruoyi/utils/sdk/dhsdk/service/impl/DahuaSDK.java | 406 ++++++++++++++++++++++++++++++++++++++------------------- 1 files changed, 267 insertions(+), 139 deletions(-) diff --git a/ard-work/src/main/java/com/ruoyi/utils/sdk/dhsdk/service/impl/DahuaSDK.java b/ard-work/src/main/java/com/ruoyi/utils/sdk/dhsdk/service/impl/DahuaSDK.java index 4ffb4d8..85e0da4 100644 --- a/ard-work/src/main/java/com/ruoyi/utils/sdk/dhsdk/service/impl/DahuaSDK.java +++ b/ard-work/src/main/java/com/ruoyi/utils/sdk/dhsdk/service/impl/DahuaSDK.java @@ -23,12 +23,14 @@ import com.ruoyi.utils.sdk.common.SdkErrorCodeEnum; import com.ruoyi.utils.sdk.dhsdk.common.ErrorCode; import com.ruoyi.utils.sdk.dhsdk.lib.NetSDKLib; +import com.ruoyi.utils.sdk.dhsdk.lib.ToolKits; import com.ruoyi.utils.sdk.dhsdk.lib.enumeration.EM_NEW_CONFIG; import com.ruoyi.utils.sdk.dhsdk.lib.enumeration.NET_EM_CFG_OPERATE_TYPE; import com.ruoyi.utils.sdk.dhsdk.lib.structure.CFG_INFRARED_INFO; import com.ruoyi.utils.sdk.dhsdk.lib.structure.CFG_VIDEO_IN_FOCUS; import com.ruoyi.utils.sdk.dhsdk.lib.structure.DH_OUT_PTZ_VIEW_RANGE_STATUS; import com.ruoyi.utils.sdk.dhsdk.module.*; +import com.sun.jna.Native; import com.sun.jna.Pointer; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -36,12 +38,14 @@ import org.springframework.stereotype.Service; import javax.annotation.Resource; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletResponse; import java.io.*; +import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.nio.file.Paths; import java.text.DecimalFormat; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.PriorityBlockingQueue; import static com.ruoyi.utils.sdk.dhsdk.lib.NetSDKLib.*; @@ -86,145 +90,156 @@ * @return */ @Override - - public boolean login(ArdCameras camera) { - LoginModule.init(disConnect, haveReConnect); - NetSDKLib.NET_DEVICEINFO_Ex m_stDeviceInfo = new NetSDKLib.NET_DEVICEINFO_Ex(); - NetSDKLib.LLong loginId = LoginModule.login(camera.getIp(), camera.getPort(), camera.getUsername(), camera.getPassword(), m_stDeviceInfo); - if (loginId.longValue() <= 0) { - camera.setChanNum(0); - camera.setLoginId(-1); - camera.setState("0"); - ardCamerasService.updateArdCameras(camera); + public AjaxResult login(ArdCameras camera) { + try { + LoginModule.init(disConnect, haveReConnect); + NetSDKLib.NET_DEVICEINFO_Ex m_stDeviceInfo = new NetSDKLib.NET_DEVICEINFO_Ex(); + NetSDKLib.LLong loginId = LoginModule.login(camera.getIp(), camera.getPort(), camera.getUsername(), camera.getPassword(), m_stDeviceInfo); + if (loginId.longValue() <= 0) { + camera.setChanNum(0); + camera.setLoginId(-1); + camera.setState("0"); + ardCamerasService.updateArdCameras(camera); + //鍒犻櫎绠$悊閫氶亾 + ardChannelService.deleteArdChannelByDeviceId(camera.getId()); + log.error("璁惧[" + camera.getIp() + ":" + camera.getPort() + "]鐧诲綍澶辫触:" + getErrorCodePrint()); + return AjaxResult.error(ErrorCode.getErrorCode(LoginModule.netsdk.CLIENT_GetLastError())); + } + if (GlobalVariable.loginMap.containsKey(camera.getId())) { + GlobalVariable.loginMap.remove(camera.getId()); + } //鍒犻櫎绠$悊閫氶亾 ardChannelService.deleteArdChannelByDeviceId(camera.getId()); - return false; - } - if (GlobalVariable.loginMap.containsKey(camera.getId())) { - GlobalVariable.loginMap.remove(camera.getId()); - } - //鍒犻櫎绠$悊閫氶亾 - ardChannelService.deleteArdChannelByDeviceId(camera.getId()); - camera.setState("1"); - camera.setChanNum(m_stDeviceInfo.byChanNum); - camera.setStartDChan(1); - camera.setLoginId((int) loginId.longValue()); - ardCamerasService.updateArdCameras(camera); - GlobalVariable.loginMap.put(camera.getId(), loginId); + camera.setState("1"); + camera.setChanNum(m_stDeviceInfo.byChanNum); + camera.setStartDChan(1); + camera.setLoginId((int) loginId.longValue()); + ardCamerasService.updateArdCameras(camera); + GlobalVariable.loginMap.put(camera.getId(), loginId); - //鑾峰彇鏈�鏂伴�氶亾 - for (int i = 1; i < m_stDeviceInfo.byChanNum + 1; i++) { - ArdChannel channel = new ArdChannel(); - NetSDKLib.AV_CFG_ChannelName av_cfg_channelName = new NetSDKLib.AV_CFG_ChannelName(); - boolean b = ConfigModule.GetNewDevConfig(loginId, i - 1, CFG_CMD_CHANNELTITLE, av_cfg_channelName); - if (b) { - String chanName = null; - try { - chanName = new String(av_cfg_channelName.szName, "GBK").trim(); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } - channel.setName(chanName); - } else { - channel.setName("閫氶亾" + i); - } - channel.setDeviceId(camera.getId()); - channel.setChanNo(i); - ardChannelService.insertArdChannel(channel); - } - //閰嶇疆鍒版祦濯掍綋 - for (int i = 1; i < m_stDeviceInfo.byChanNum + 1; i++) { - String name = camera.getId() + "_" + i; - String rtspSource = "rtsp://" + camera.getUsername() + ":" + camera.getPassword() + "@" + camera.getIp() + ":" + camera.getRtspPort() + "/cam/realmonitor?channel=" + i + "&subtype=0"; - Vtdu vtdu = vtduService.selectVtduByName(name); - if (vtdu != null) { - vtduService.deleteVtduByName(name); - } - //娣诲姞鍒版祦濯掍綋 - CameraCmd cmd = new CameraCmd(camera.getId(), i); - Map<String, Object> videoCompressionCfg = getVideoCompressionCfg(cmd); - vtdu = new Vtdu(); - if (videoCompressionCfg.get("videoEncType") != null) { - if (videoCompressionCfg.get("videoEncType").equals("鏍囧噯h264")) { - vtdu.setIsCode("0");//榛樿涓嶈浆鐮� + //鑾峰彇鏈�鏂伴�氶亾 + for (int i = 1; i < m_stDeviceInfo.byChanNum + 1; i++) { + ArdChannel channel = new ArdChannel(); + NetSDKLib.AV_CFG_ChannelName av_cfg_channelName = new NetSDKLib.AV_CFG_ChannelName(); + boolean b = ConfigModule.GetNewDevConfig(loginId, i - 1, CFG_CMD_CHANNELTITLE, av_cfg_channelName); + if (b) { + String chanName = null; + try { + chanName = new String(av_cfg_channelName.szName, "GBK").trim(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + channel.setName(chanName); } else { - vtdu.setIsCode("1");//榛樿杞爜 + channel.setName("閫氶亾" + i); } - } else { - vtdu.setIsCode("0");//榛樿涓嶈浆鐮� + channel.setDeviceId(camera.getId()); + channel.setChanNo(i); + ardChannelService.insertArdChannel(channel); } - vtdu.setRtspSource(rtspSource); - vtdu.setName(camera.getId() + "_" + i); - vtdu.setMode("1");//榛樿CPU杞В鐮� - vtdu.setCameraId(camera.getId()); - vtduService.insertVtdu(vtdu); - } + //閰嶇疆鍒版祦濯掍綋 + for (int i = 1; i < m_stDeviceInfo.byChanNum + 1; i++) { + String name = camera.getId() + "_" + i; + String rtspSource = "rtsp://" + camera.getUsername() + ":" + camera.getPassword() + "@" + camera.getIp() + ":" + camera.getRtspPort() + "/cam/realmonitor?channel=" + i + "&subtype=0"; + Vtdu vtdu = vtduService.selectVtduByName(name); + if (vtdu != null) { + vtduService.deleteVtduByName(name); + } + //娣诲姞鍒版祦濯掍綋 + CameraCmd cmd = new CameraCmd(camera.getId(), i); + Map<String, Object> videoCompressionCfg = getVideoCompressionCfg(cmd); + vtdu = new Vtdu(); + if (videoCompressionCfg.get("videoEncType") != null) { + if (videoCompressionCfg.get("videoEncType").equals("鏍囧噯h264")) { + vtdu.setIsCode("0");//榛樿涓嶈浆鐮� + } else { + vtdu.setIsCode("1");//榛樿杞爜 + } + } else { + vtdu.setIsCode("0");//榛樿涓嶈浆鐮� + } + vtdu.setRtspSource(rtspSource); + vtdu.setName(camera.getId() + "_" + i); + vtdu.setMode("1");//榛樿CPU杞В鐮� + vtdu.setCameraId(camera.getId()); + vtduService.insertVtdu(vtdu); + } - //鍒涘缓寮曞闃熷垪 - //鍒涘缓寮曞闃熷垪 - if (camera.getCamAlarmGuideEnable() == 1) { - if (!GuidePriorityQueue.cameraQueueMap.containsKey(camera.getId())) { - Comparator<GuideTask> comparator = GuidePriorityQueue.getComparator(); - PriorityBlockingQueue<GuideTask> priorityQueue = new PriorityBlockingQueue<>(1000, comparator); - GuidePriorityQueue.cameraQueueMap.put(camera.getId(), priorityQueue); + //鍒涘缓寮曞闃熷垪 + if (camera.getCamAlarmGuideEnable() != null && camera.getCamAlarmGuideEnable() == 1) { + if (!GuidePriorityQueue.cameraQueueMap.containsKey(camera.getId())) { + Comparator<GuideTask> comparator = GuidePriorityQueue.getComparator(); + PriorityBlockingQueue<GuideTask> priorityQueue = new PriorityBlockingQueue<>(1000, comparator); + GuidePriorityQueue.cameraQueueMap.put(camera.getId(), priorityQueue); + } + //鍚姩闃熷垪澶勭悊鍣� + queueHandler.process(camera.getId()); } - //鍚姩闃熷垪澶勭悊鍣� - queueHandler.process(camera.getId()); + return AjaxResult.success("璁惧鐧诲綍鎴愬姛"); + } catch (Exception ex) { + log.error("璁惧鐧诲綍寮傚父:" + ex.getMessage()); + return AjaxResult.error("璁惧鐧诲綍寮傚父:" + ex.getMessage()); } - return true; } @Override @Async("loginExecutor") - public void asyncLogin(ArdCameras camera) { - LoginModule.init(disConnect, haveReConnect); - NetSDKLib.NET_DEVICEINFO_Ex m_stDeviceInfo = new NetSDKLib.NET_DEVICEINFO_Ex(); - NetSDKLib.LLong loginId = LoginModule.login(camera.getIp(), camera.getPort(), camera.getUsername(), camera.getPassword(), m_stDeviceInfo); - if (loginId.longValue() <= 0) { - camera.setChanNum(0); - camera.setLoginId(-1); - camera.setState("0"); - ardCamerasService.updateArdCameras(camera); + public AjaxResult asyncLogin(ArdCameras camera) { + try { + LoginModule.init(disConnect, haveReConnect); + NetSDKLib.NET_DEVICEINFO_Ex m_stDeviceInfo = new NetSDKLib.NET_DEVICEINFO_Ex(); + NetSDKLib.LLong loginId = LoginModule.login(camera.getIp(), camera.getPort(), camera.getUsername(), camera.getPassword(), m_stDeviceInfo); + if (loginId.longValue() <= 0) { + camera.setChanNum(0); + camera.setLoginId(-1); + camera.setState("0"); + ardCamerasService.updateArdCameras(camera); + //鍒犻櫎绠$悊閫氶亾 + ardChannelService.deleteArdChannelByDeviceId(camera.getId()); + log.error("璁惧[" + camera.getIp() + ":" + camera.getPort() + "]鐧诲綍澶辫触:" + getErrorCodePrint()); + return AjaxResult.error(ErrorCode.getErrorCode(LoginModule.netsdk.CLIENT_GetLastError())); + } + if (GlobalVariable.loginMap.containsKey(camera.getId())) { + GlobalVariable.loginMap.remove(camera.getId()); + } //鍒犻櫎绠$悊閫氶亾 ardChannelService.deleteArdChannelByDeviceId(camera.getId()); - return; - } - if (GlobalVariable.loginMap.containsKey(camera.getId())) { - GlobalVariable.loginMap.remove(camera.getId()); - } - //鍒犻櫎绠$悊閫氶亾 - ardChannelService.deleteArdChannelByDeviceId(camera.getId()); - camera.setState("1"); - camera.setChanNum(m_stDeviceInfo.byChanNum); - camera.setStartDChan(1); - camera.setLoginId((int) loginId.longValue()); - ardCamerasService.updateArdCameras(camera); - GlobalVariable.loginMap.put(camera.getId(), loginId); + camera.setState("1"); + camera.setChanNum(m_stDeviceInfo.byChanNum); + camera.setStartDChan(1); + camera.setLoginId((int) loginId.longValue()); + ardCamerasService.updateArdCameras(camera); + GlobalVariable.loginMap.put(camera.getId(), loginId); - //鑾峰彇鏈�鏂伴�氶亾 - for (int i = 1; i < m_stDeviceInfo.byChanNum + 1; i++) { - ArdChannel channel = new ArdChannel(); - NetSDKLib.AV_CFG_ChannelName av_cfg_channelName = new NetSDKLib.AV_CFG_ChannelName(); - boolean b = ConfigModule.GetNewDevConfig(loginId, i - 1, CFG_CMD_CHANNELTITLE, av_cfg_channelName); - if (b) { - String chanName = null; - try { - chanName = new String(av_cfg_channelName.szName, "GBK").trim(); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); + //鑾峰彇鏈�鏂伴�氶亾 + for (int i = 1; i < m_stDeviceInfo.byChanNum + 1; i++) { + ArdChannel channel = new ArdChannel(); + NetSDKLib.AV_CFG_ChannelName av_cfg_channelName = new NetSDKLib.AV_CFG_ChannelName(); + boolean b = ConfigModule.GetNewDevConfig(loginId, i - 1, CFG_CMD_CHANNELTITLE, av_cfg_channelName); + if (b) { + String chanName = null; + try { + chanName = new String(av_cfg_channelName.szName, "GBK").trim(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + channel.setName(chanName); + } else { + channel.setName("閫氶亾" + i); } - channel.setName(chanName); - } else { - channel.setName("閫氶亾" + i); + channel.setDeviceId(camera.getId()); + channel.setChanNo(i); + ardChannelService.insertArdChannel(channel); } - channel.setDeviceId(camera.getId()); - channel.setChanNo(i); - ardChannelService.insertArdChannel(channel); + //閰嶇疆鍒版祦濯掍綋 + addVtdu(camera); + //鍒涘缓寮曞闃熷垪 + createGuideQueue(camera); + return AjaxResult.success("鐧诲綍鎴愬姛"); + } catch (Exception ex) { + log.error("鐧诲綍寮傚父:" + ex.getMessage()); + return AjaxResult.error("鐧诲綍寮傚父:" + ex.getMessage()); } - //閰嶇疆鍒版祦濯掍綋 - addVtdu(camera); - //鍒涘缓寮曞闃熷垪 - createGuideQueue(camera); } //娣诲姞鍒版祦濯掍綋 @@ -259,16 +274,12 @@ //鍒涘缓寮曞闃熷垪 private void createGuideQueue(ArdCameras camera) { - if (camera.getCamAlarmGuideEnable() != null) { - if (camera.getCamAlarmGuideEnable() == 1) { - if (!GuidePriorityQueue.cameraQueueMap.containsKey(camera.getId())) { - Comparator<GuideTask> comparator = GuidePriorityQueue.getComparator(); - PriorityBlockingQueue<GuideTask> priorityQueue = new PriorityBlockingQueue<>(1000, comparator); - GuidePriorityQueue.cameraQueueMap.put(camera.getId(), priorityQueue); - //鍚姩闃熷垪澶勭悊鍣� - queueHandler.process(camera.getId()); - } - } + if (!GuidePriorityQueue.cameraQueueMap.containsKey(camera.getId())) { + Comparator<GuideTask> comparator = GuidePriorityQueue.getComparator(); + PriorityBlockingQueue<GuideTask> priorityQueue = new PriorityBlockingQueue<>(1000, comparator); + GuidePriorityQueue.cameraQueueMap.put(camera.getId(), priorityQueue); + //鍚姩闃熷垪澶勭悊鍣� + queueHandler.process(camera.getId()); } } @@ -539,6 +550,101 @@ } + @Override + public AjaxResult localCapture(CameraCmd cmd) { + try { + String cameraId = cmd.getCameraId(); + Integer chanNo = cmd.getChanNo(); + if (!GlobalVariable.loginMap.containsKey(cameraId)) { + return AjaxResult.error("璁惧鏈櫥褰�"); + } + LLong loginId = (LLong) GlobalVariable.loginMap.get(cameraId); + String imagePath = FileUtils.createFile("D:/LocalCaptureTemp/" + cameraId + ".jpeg"); + fCaptureReceiveCB1 m_CaptureReceiveCB = new fCaptureReceiveCB1(imagePath); + CapturePictureModule.setSnapRevCallBack(m_CaptureReceiveCB); + boolean b = CapturePictureModule.remoteCapturePicture(loginId, chanNo - 1); + if (b) { + Thread.sleep(200); + byte[] imageBytes = Files.readAllBytes(Paths.get(imagePath)); + //OutputStream outputStream = response.getOutputStream(); + //outputStream.write(imageBytes); + // 鍒涘缓ByteBuffer骞跺皢瀛楄妭鏁扮粍鍐欏叆鍏朵腑 + //ByteBuffer jpegBuffer = ByteBuffer.wrap(imageBytes); + //Base64.Encoder decoder = Base64.getEncoder(); + //String png_base64 = decoder.encodeToString(jpegBuffer.array());//杞崲鎴恇ase64涓� + return AjaxResult.success(imageBytes); + } else { + return AjaxResult.error("鏈湴鎶撳浘澶辫触"); + } + } catch (Exception ex) { + return AjaxResult.error("鏈湴鎶撳浘寮傚父:" + ex); + } + } + + //鏈湴褰曞儚寮�濮� + @Override + public AjaxResult localRecordStart(CameraCmd cmd) { + try { + String cameraId = cmd.getCameraId(); + Integer chanNo = cmd.getChanNo(); + String path = FileUtils.createFile("D:/LocalRecordTemp/" + cameraId + ".mp4"); + if (!GlobalVariable.loginMap.containsKey(cameraId)) { + return AjaxResult.error("璁惧鏈櫥褰�"); + } + LLong loginId = (LLong) GlobalVariable.loginMap.get(cameraId); + LLong lRealHandle; + if (GlobalVariable.previewMap.containsKey(cameraId)) { + lRealHandle = new LLong(GlobalVariable.previewMap.get(cameraId)); + RealPlayModule.stopRealPlay(lRealHandle); + GlobalVariable.previewMap.remove(cameraId); + log.debug("鍋滄褰撳墠褰曞儚"); + } + lRealHandle = RealPlayModule.startRealPlay(loginId, chanNo - 1, path); + if (lRealHandle.longValue() <= 0) { + log.error("鍙栨祦澶辫触" + getErrorCodePrint()); + } + log.debug("鏈湴褰曞儚寮�濮嬪彇娴佹垚鍔�"); + GlobalVariable.previewMap.put(cameraId, lRealHandle.intValue()); + GlobalVariable.threadMap.put(cameraId, Thread.currentThread().getName()); + //if (!netsdk.CLIENT_SaveRealData(lRealHandle, path)) { + // log.error("淇濆瓨瑙嗛鏂囦欢鍒颁复鏃舵枃浠跺す澶辫触 閿欒鐮佷负: " +getErrorCodePrint()); + // return false; + //} + log.debug("鏈湴褰曞儚寮�濮�"); + return AjaxResult.success("鏈湴褰曞儚寮�濮�,褰曞儚ID:"+lRealHandle); + } catch (Exception ex) { + log.error("鏈湴褰曞儚寮�濮嬪紓甯�" + ex.getMessage()); + return AjaxResult.error("鏈湴褰曞儚寮�濮嬪紓甯�" + ex.getMessage()); + } + } + //鏈湴褰曞儚鍋滄 + @Override + public AjaxResult localRecordStop(CameraCmd cmd) { + try { + String cameraId = cmd.getCameraId(); + if (!GlobalVariable.loginMap.containsKey(cameraId)) { + return AjaxResult.error("璁惧鏈櫥褰�"); + } + LLong lRealHandle; + if (GlobalVariable.previewMap.containsKey(cameraId)) { + lRealHandle = new LLong(GlobalVariable.previewMap.get(cameraId)); + RealPlayModule.stopRealPlay(lRealHandle); + GlobalVariable.previewMap.remove(cameraId); + log.debug("鏈湴褰曞儚鍋滄"); + String recordPath = FileUtils.createFile("D:/LocalRecordTemp/" + cameraId + ".mp4"); + byte[] imageBytes = Files.readAllBytes(Paths.get(recordPath)); + // OutputStream outputStream = response.getOutputStream(); + // outputStream.write(imageBytes); + return AjaxResult.success(imageBytes); + } + else { + return AjaxResult.error("璁惧鏈紑濮嬪綍鍍�"); + } + } catch (Exception ex) { + log.error("鏈湴褰曞儚鍋滄寮傚父" + ex.getMessage()); + return AjaxResult.error("鏈湴褰曞儚鍋滄寮傚父" + ex.getMessage()); + } + } /** * @鎻忚堪 鐭椂褰曞儚 @@ -728,7 +834,6 @@ } LLong loginId = (LLong) GlobalVariable.loginMap.get(cameraId); try { - boolean bool = netsdk.CLIENT_DHPTZControlEx(loginId, chanNo - 1, NET_PTZ_POINT_MOVE_CONTROL, 0, PresetIndex, 0, 0); if (!bool) { log.error("杞嚦棰勭疆鐐瑰け璐�:" + getErrorCodePrint()); @@ -864,10 +969,6 @@ return AjaxResult.success("璇ュ姛鑳芥湭瀹炵幇"); } - @Override - public AjaxResult captureJPEGPicture(CameraCmd cmd) { - return AjaxResult.success("璇ュ姛鑳芥湭瀹炵幇"); - } //鍒囨崲閫忛浘 @Override @@ -1114,4 +1215,31 @@ } } + // 鎶撳浘鎺ユ敹鍥炶皟:褰撴姄鍥炬垚鍔燂紝sdk浼氳皟鐢ㄨ鍑芥暟 + public static class fCaptureReceiveCB1 implements NetSDKLib.fSnapRev { + private String picPath; + public fCaptureReceiveCB1(String picPath) { + this.picPath = picPath; + } + public void invoke(LLong lLoginID, Pointer pBuf, int RevLen, int EncodeType, int CmdSerial, Pointer dwUser) { + if (pBuf != null && RevLen > 0) { + byte[] buf = pBuf.getByteArray(0, RevLen); + //瀛樺偍鍒版湰鍦� + InputStream input = new ByteArrayInputStream(buf); + OutputStream output = null; + try { + output = new FileOutputStream(picPath); + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = input.read(buffer)) != -1) { + output.write(buffer, 0, bytesRead); + } + input.close(); + output.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } } -- Gitblit v1.9.3