From 602bf5d3b6d916f5bdc3acd6b041251275d31109 Mon Sep 17 00:00:00 2001
From: whyclxw <810412026@qq.com>
Date: 星期四, 05 六月 2025 10:49:50 +0800
Subject: [PATCH] 标准❀参数管理

---
 src/main/java/com/whyc/util/CommonUtil.java                            |    4 
 src/main/java/com/whyc/controller/PwrdevAlarmParamStandController.java |   50 ++++++
 src/main/java/com/whyc/service/PwrdevAlarmParamStandService.java       |   81 ++++++++++
 src/main/java/com/whyc/mapper/PwrdevAlarmParamStandMapper.java         |    6 
 src/main/java/com/whyc/service/PwrdevAlarmParamService.java            |   11 +
 src/main/java/com/whyc/mapper/PwrdevAlarmParamMapper.java              |    6 
 src/main/java/com/whyc/util/FileUtil.java                              |  300 +++++++++++++++++++++++++++++++++++++
 7 files changed, 455 insertions(+), 3 deletions(-)

diff --git a/src/main/java/com/whyc/controller/PwrdevAlarmParamStandController.java b/src/main/java/com/whyc/controller/PwrdevAlarmParamStandController.java
new file mode 100644
index 0000000..9050004
--- /dev/null
+++ b/src/main/java/com/whyc/controller/PwrdevAlarmParamStandController.java
@@ -0,0 +1,50 @@
+package com.whyc.controller;
+
+import com.whyc.dto.Response;
+import com.whyc.pojo.db_pwrdev_alarm.PwrdevAlarmParamStand;
+import com.whyc.service.PwrdevAlarmParamStandService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+
+@RestController
+@Api(tags = "鏍囧噯鍙傛暟绠$悊")
+@RequestMapping("stand")
+public class PwrdevAlarmParamStandController {
+    @Autowired
+    private PwrdevAlarmParamStandService service;
+
+    @ApiOperation(value = "鏌ヨ鏍囧噯鍙傛暟")
+    @GetMapping("getPwrStandParam")
+    public Response getPwrStandParam(@RequestParam Integer powerType){
+        return service.getPwrStandParam(powerType);
+    }
+
+    @ApiOperation(value = "璁剧疆鏍囧噯鍙傛暟")
+    @PostMapping("setPwrStandParam")
+    public Response setPwrStandParam(@RequestBody PwrdevAlarmParamStand stand){
+        return service.setPwrStandParam(stand);
+    }
+
+    @ApiOperation(value = "鏌ヨ瑙勮寖鏂囦欢")
+    @GetMapping("getStandFile")
+    public Response getStandFile(@RequestParam Integer powerType,@RequestParam String fileName){
+        return service.getStandFile(powerType,fileName);
+    }
+    @ApiOperation(value = "涓婁紶瑙勮寖鏂囦欢")
+    @PostMapping("uploadStandFile")
+    public Response uploadStandFile(@RequestParam MultipartFile multipartFile,@RequestParam String num) throws IOException {
+        return service.uploadStandFile(multipartFile,num);
+    }
+
+    @ApiOperation(value = "娣诲姞鏍囧噯鍙傛暟")
+    @PostMapping("addPwrStandParam")
+    public Response addPwrStandParam(@RequestBody PwrdevAlarmParamStand stand){
+        return service.addPwrStandParam(stand);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/whyc/mapper/PwrdevAlarmParamMapper.java b/src/main/java/com/whyc/mapper/PwrdevAlarmParamMapper.java
new file mode 100644
index 0000000..926853a
--- /dev/null
+++ b/src/main/java/com/whyc/mapper/PwrdevAlarmParamMapper.java
@@ -0,0 +1,6 @@
+package com.whyc.mapper;
+
+import com.whyc.pojo.db_pwrdev_alarm.PwrdevAlarmParam;
+
+public interface PwrdevAlarmParamMapper extends CustomMapper<PwrdevAlarmParam>{
+}
\ No newline at end of file
diff --git a/src/main/java/com/whyc/mapper/PwrdevAlarmParamStandMapper.java b/src/main/java/com/whyc/mapper/PwrdevAlarmParamStandMapper.java
new file mode 100644
index 0000000..f3ee525
--- /dev/null
+++ b/src/main/java/com/whyc/mapper/PwrdevAlarmParamStandMapper.java
@@ -0,0 +1,6 @@
+package com.whyc.mapper;
+
+import com.whyc.pojo.db_pwrdev_alarm.PwrdevAlarmParamStand;
+
+public interface PwrdevAlarmParamStandMapper extends CustomMapper<PwrdevAlarmParamStand>{
+}
\ No newline at end of file
diff --git a/src/main/java/com/whyc/service/PwrdevAlarmParamService.java b/src/main/java/com/whyc/service/PwrdevAlarmParamService.java
new file mode 100644
index 0000000..0a9349b
--- /dev/null
+++ b/src/main/java/com/whyc/service/PwrdevAlarmParamService.java
@@ -0,0 +1,11 @@
+package com.whyc.service;
+
+import com.whyc.mapper.PwrdevAlarmParamMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class PwrdevAlarmParamService {
+    @Autowired(required = false)
+    private PwrdevAlarmParamMapper mapper;
+}
\ No newline at end of file
diff --git a/src/main/java/com/whyc/service/PwrdevAlarmParamStandService.java b/src/main/java/com/whyc/service/PwrdevAlarmParamStandService.java
new file mode 100644
index 0000000..ad6a7b9
--- /dev/null
+++ b/src/main/java/com/whyc/service/PwrdevAlarmParamStandService.java
@@ -0,0 +1,81 @@
+package com.whyc.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import com.whyc.dto.Response;
+import com.whyc.mapper.PwrdevAlarmParamStandMapper;
+import com.whyc.pojo.db_pwrdev_alarm.PwrdevAlarmParamStand;
+import com.whyc.util.ActionUtil;
+import com.whyc.util.DateUtil;
+import com.whyc.util.FileUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.List;
+
+@Service
+public class PwrdevAlarmParamStandService {
+    @Autowired(required = false)
+    private PwrdevAlarmParamStandMapper mapper;
+    //鏌ヨ鏍囧噯鍙傛暟
+    public Response getPwrStandParam(Integer powerType) {
+        QueryWrapper queryWrapper = new QueryWrapper();
+        queryWrapper.eq("power_type",powerType);
+        queryWrapper.orderByAsc("num");
+        List<PwrdevAlarmParamStand> list=mapper.selectList(queryWrapper);
+        return new Response().setII(1,list!=null,list,"鏌ヨ鏍囧噯鍙傛暟");
+    }
+    //鏌ヨ瑙勮寖鏂囦欢
+    public Response getStandFile(Integer powerType, String fileName) {
+        QueryWrapper queryWrapper = new QueryWrapper();
+        queryWrapper.select("file_name","stand_file_path");
+        queryWrapper.eq("power_type",powerType);
+        queryWrapper.like("file_name",fileName);
+        List<PwrdevAlarmParamStand> list=mapper.selectList(queryWrapper);
+        return new Response().setII(1,list!=null,list,"鏌ヨ瑙勮寖鏂囦欢");
+    }
+    //璁剧疆鏍囧噯鍙傛暟
+    public Response setPwrStandParam(PwrdevAlarmParamStand stand) {
+        UpdateWrapper wrapper=new UpdateWrapper();
+        if(stand.getAlarmLimith()!=null){
+            wrapper.set("alarm_limith",stand.getAlarmLimith());
+        }
+        if(stand.getAlarmLimitl()!=null){
+            wrapper.set("alarm_limitl",stand.getAlarmLimitl());
+        }
+        if(stand.getAlarmLimithUpeper()!=null){
+            wrapper.set("alarm_limith_upeper",stand.getAlarmLimithUpeper());
+        }
+        if(stand.getAlarmLimitlLower()!=null){
+            wrapper.set("alarm_limitl_lower",stand.getAlarmLimitlLower());
+        }
+        if(stand.getBasisVal()!=null){
+            wrapper.set("basis_val",stand.getBasisVal());
+        }
+        wrapper.eq("num",stand.getNum());
+        int flag=mapper.update((PwrdevAlarmParamStand) ActionUtil.objeNull,wrapper);
+        return new Response().set(1,flag>0,flag>0?"璁剧疆鎴愬姛":"璁剧疆澶辫触");
+    }
+    //涓婁紶瑙勮寖鏂囦欢
+    public Response uploadStandFile(MultipartFile multipartFile, String num) throws IOException {
+        Date now = new Date();
+        String originalFilename = multipartFile.getOriginalFilename();
+        String[] fileNameSplit = originalFilename.split("\\.");
+        String dateFormat = DateUtil.YYYY_MM_DD_HH_MM_SS_UNION.format(now);
+        String newFileName = fileNameSplit[0]+"_"+dateFormat+"."+fileNameSplit[1];
+        String fileUrlTemp = FileUtil.saveFile(multipartFile,"/stand/"+newFileName);
+        UpdateWrapper wrapper=new UpdateWrapper();
+        wrapper.set("stand_file_path",fileUrlTemp);
+        wrapper.eq("num",num);
+        int flag=mapper.update((PwrdevAlarmParamStand) ActionUtil.objeNull,wrapper);
+        return new Response().set(1,flag>0,flag>0?"涓婁紶鎴愬姛":"涓婁紶澶辫触");
+    }
+    //娣诲姞鏍囧噯鍙傛暟
+    public Response addPwrStandParam(PwrdevAlarmParamStand stand) {
+        int flag=mapper.insert(stand);
+        return new Response().set(1,flag>0,flag>0?"娣诲姞鎴愬姛":"娣诲姞澶辫触");
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/whyc/util/CommonUtil.java b/src/main/java/com/whyc/util/CommonUtil.java
index 4145c80..f876076 100644
--- a/src/main/java/com/whyc/util/CommonUtil.java
+++ b/src/main/java/com/whyc/util/CommonUtil.java
@@ -40,10 +40,10 @@
         String baseDirPath;
         if(YamlProperties.runModel == 1) {
             //寮�鍙戣矾寰�
-            baseDirPath = jarFile.getParentFile().toString()+File.separator+"fg_file"+File.separator;
+            baseDirPath = jarFile.getParentFile().toString()+File.separator+"pis_file"+File.separator;
         }else {
             //鎵撳寘璺緞
-            baseDirPath = jarFile.toString()+File.separator+"fg_file"+File.separator;
+            baseDirPath = jarFile.toString()+File.separator+"pis_file"+File.separator;
         }
         return baseDirPath;
     }
diff --git a/src/main/java/com/whyc/util/FileUtil.java b/src/main/java/com/whyc/util/FileUtil.java
index dfc3a08..e237f3c 100644
--- a/src/main/java/com/whyc/util/FileUtil.java
+++ b/src/main/java/com/whyc/util/FileUtil.java
@@ -1,8 +1,77 @@
 package com.whyc.util;
 
-import java.io.File;
+import org.apache.commons.compress.archivers.ArchiveEntry;
+import org.apache.commons.compress.archivers.ArchiveException;
+import org.apache.commons.compress.archivers.ArchiveInputStream;
+import org.apache.commons.compress.archivers.ArchiveStreamFactory;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.net.URLEncoder;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
 
 public class FileUtil {
+
+
+    public static List<String> getStaticFilePath(File file, List<String> list){
+
+        //濡傛灉鏄枃浠剁殑鎯呭喌
+        if (file.isFile()){
+            list.add(file.getAbsolutePath());
+        }else{
+            //濡傛灉鏄洰褰曠殑鎯呭喌
+            //鍒涘缓涓�涓狥ile鏁扮粍鏉ュ瓨鍌ㄥ綋鍓嶇洰褰曚笅鎵�鏈夋枃浠跺拰鐩綍鐨勭粷瀵硅矾寰�
+            File[] files = file.listFiles();
+            //寰幆閬嶅巻files
+            for (File fileTemp : files){
+                if(fileTemp.getName().contains(".zip")){
+                    continue;
+                }
+                //瀛愮骇鏄洰褰�
+                if (fileTemp.isDirectory()){
+                    //閫掑綊鍐嶆杩涜鍒ゆ柇
+                    getStaticFilePath(fileTemp, list);
+                }else{
+                    //瀛愮骇鏄枃浠�
+                    String absolutePath = fileTemp.getAbsolutePath();
+                    list.add(absolutePath);
+                    //System.out.println(temp + "鏂囦欢 :" + fileTemp.getName() + "\t");
+                }
+            }
+        }
+        return list;
+    }
+
+    public static List<String> getStaticFilePathII(File file, List<String> list){
+
+        //濡傛灉鏄枃浠剁殑鎯呭喌
+        if (file.isFile()){
+            list.add(file.getAbsolutePath());
+        }else{
+            //濡傛灉鏄洰褰曠殑鎯呭喌
+            //鍒涘缓涓�涓狥ile鏁扮粍鏉ュ瓨鍌ㄥ綋鍓嶇洰褰曚笅鎵�鏈夋枃浠跺拰鐩綍鐨勭粷瀵硅矾寰�
+            File[] files = file.listFiles();
+            //寰幆閬嶅巻files
+            for (File fileTemp : files){
+                //瀛愮骇鏄洰褰�
+                if (fileTemp.isDirectory()){
+                    //閫掑綊鍐嶆杩涜鍒ゆ柇
+                    getStaticFilePathII(fileTemp, list);
+                }else{
+                    //瀛愮骇鏄枃浠�
+                    String absolutePath = fileTemp.getAbsolutePath();
+                    list.add(absolutePath);
+                    //System.out.println(temp + "鏂囦欢 :" + fileTemp.getName() + "\t");
+                }
+            }
+        }
+        return list;
+    }
+
     public static Boolean deleteFile(File file) {
         //鍒ゆ柇鏂囦欢涓嶄负null鎴栨枃浠剁洰褰曞瓨鍦�
         if (file == null || !file.exists()) {
@@ -27,4 +96,233 @@
         file.delete();
         return true;
     }
+
+    public static void download(HttpServletResponse resp,String inFilePath,String outFileFullName){
+        try {
+            // 杞爜闃叉涔辩爜
+            //resp.addHeader("Content-Disposition", "attachment;filename=" + new String(softwareName.getBytes("UTF-8"), "ISO8859-1"));
+            resp.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode ( outFileFullName, "utf-8"));
+            OutputStream out = resp.getOutputStream();
+            FileInputStream in = new FileInputStream(inFilePath);
+            int len=0;
+            byte[] buffer =new byte[1024];
+            //7. 灏嗙紦鍐插尯涓殑鏁版嵁杈撳嚭
+            while ((len=in.read(buffer))>0){
+                out.write(buffer,0,len);
+            }
+            in.close();
+            out.close();
+        } catch (FileNotFoundException | UnsupportedEncodingException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static String saveFile(MultipartFile multipartFile,String fileName) throws IOException {
+        String rootFile = CommonUtil.getRootFile();
+
+        String filePath = rootFile + fileName;
+        File file = new File(filePath);
+        File parentFile = file.getParentFile();
+        if(!parentFile.exists()){
+            parentFile.mkdirs();
+        }
+        //瀛樺偍
+        multipartFile.transferTo(file);
+        return "pis_file"+fileName;
+    }
+
+    /**
+     * 澶嶅埗鏂囦欢澶瑰唴鐨勬墍鏈夋枃浠跺埌鍙︿竴涓枃浠跺す
+     */
+    public static void copyDirectory(File source, File destination) {
+        if (source.isDirectory()) {
+            if (!destination.exists()) {
+                destination.mkdir();
+            }
+            for (File file : source.listFiles()) {
+                copyDirectory(file, new File(destination, file.getName()));
+            }
+        } else {
+            try (FileInputStream inputStream = new FileInputStream(source);
+                 FileOutputStream outputStream = new FileOutputStream(destination)) {
+                byte[] buffer = new byte[1024];
+                int length;
+                while ((length = inputStream.read(buffer)) > 0) {
+                    outputStream.write(buffer, 0, length);
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    /**
+     * 瑙e帇N灞�
+     * @param compressedFileUrl doc_file/xxx/xxx.zip 鎴栬�� rar
+     * @return
+     */
+    public static List<String> decompress(String compressedFileUrl) throws ArchiveException, IOException, InterruptedException {
+        List<Object> resList = decompressOne(compressedFileUrl);
+        File outputFolderFile = (File) resList.get(0);
+        List<String>  fileList = (List<String>) resList.get(1);
+        boolean existCompressedFile = false;
+        int checkDecompress = 0;
+        //閬嶅巻鏂囦欢鍒楄〃锛屽垽鏂槸鍚﹀瓨鍦ㄥ帇缂╂枃浠�
+        for(String tempFileName:fileList){
+            if(tempFileName.endsWith("zip") || tempFileName.endsWith("rar")){
+                //瀛樺湪鍘嬬缉鏂囦欢,瑙e帇涓�娆�
+                decompressOne(tempFileName);
+                File file = new File(tempFileName);
+                //瑙i櫎鏂囦欢鍗犵敤骞跺垹闄ゆ枃浠�
+                //boolean delete = file.delete();
+                Files.delete(file.toPath());
+                existCompressedFile = true;
+            }
+        }
+        //濡傛灉瀛樺湪鍘嬬缉鏂囦欢骞跺凡瑙e帇,鍒欓渶瑕佹鏌ヤ竴娆℃槸鍚﹁繕鏈夊帇缂╂枃浠�
+        if(existCompressedFile){
+            checkDecompress ++;
+        }
+        for (int i = 0; i < checkDecompress; i++) {
+            decompress(compressedFileUrl);
+        }
+
+        List<String> finalList = new LinkedList<>();
+        getStaticFilePathII(outputFolderFile,finalList);
+        return finalList;
+    }
+
+    /**
+     * 瑙e帇涓�灞�
+     * @param compressedFileUrl doc_file/xxx/xxx.zip 鎴栬�� rar
+     * @return
+     */
+    public static List<Object> decompressOne(String compressedFileUrl) throws IOException, ArchiveException, InterruptedException {
+        List<Object> resList = new LinkedList<>();
+        String projectDir = CommonUtil.getProjectDir() + File.separator;
+        String fullFilePath;
+        String separator = File.separator;
+        String outputFolderSuffix;
+        if(compressedFileUrl.startsWith(projectDir)) { // 闈炵涓�灞傝В鍘�,宸茬粡鏄叏璺緞
+            fullFilePath = compressedFileUrl;
+            compressedFileUrl = compressedFileUrl.substring(compressedFileUrl.indexOf("decompress")+11);
+            //outputFolderSuffix = compressedFileUrl.substring(0,compressedFileUrl.lastIndexOf(separator)) + separator + "decompress_" + compressedFileUrl.substring(compressedFileUrl.lastIndexOf(separator)+1);
+            outputFolderSuffix = compressedFileUrl +"_decompress";
+        }else{ //绗竴灞傝В鍘�
+            fullFilePath = projectDir + compressedFileUrl;
+            outputFolderSuffix = compressedFileUrl.replace(separator, "@");
+        }
+        File file = new File(fullFilePath);
+
+
+        String outputFolder = CommonUtil.getRootFile() + separator + "decompress" + separator + outputFolderSuffix;
+        File outputFolderFile = new File(outputFolder);
+        if(!outputFolderFile.exists()){
+            outputFolderFile.mkdirs();
+            if(compressedFileUrl.endsWith("zip")){
+                decompressZip(file, outputFolder);
+            }else { //rar
+                decompressRar(file, outputFolder);
+            }
+        }
+        //杩斿洖鏂囦欢澶规墍鏈夋枃浠�
+        LinkedList<String> list = new LinkedList<>();
+        getStaticFilePathII(outputFolderFile,list);
+        resList.add(outputFolderFile);
+        resList.add(list);
+        return resList;
+    }
+
+    private static void decompressZip(File file, String outputFolder) throws IOException, ArchiveException {
+        ArchiveInputStream ais = new ArchiveStreamFactory("GBK").createArchiveInputStream("zip", new FileInputStream(file));
+        ArchiveEntry entry;
+        while ((entry = ais.getNextEntry()) != null) {
+            if (!ais.canReadEntryData(entry) || entry.isDirectory()) {
+                continue;
+            }
+            byte[] buffer = new byte[4096];
+            int bytesRead;
+            String entryName = entry.getName();
+            //if(entryName.contains(File.separator)){
+            if(entryName.contains("/")){
+                String entryNameDir = entryName.substring(0, entryName.lastIndexOf("/"));
+                String entryDirStr = outputFolder + File.separator + entryNameDir;
+                File entryDir = new File(entryDirStr);
+                if(!entryDir.exists()){
+                    entryDir.mkdirs();
+                }
+            }
+
+            OutputStream outputStream = new FileOutputStream(new File(outputFolder + File.separator + entryName));
+
+            while ((bytesRead = ais.read(buffer)) > -1) {
+                outputStream.write(buffer, 0, bytesRead);
+            }
+
+            //鍏虫祦
+            outputStream.close();
+        }
+        //鍏虫祦
+        ais.close();
+
+    }
+    private static void decompressRar(File file, String outputFolder) throws IOException, InterruptedException {
+        String winrarPath = "C:\\Program Files\\WinRAR\\WinRAR.exe";
+        String cmd = winrarPath + " X " + file + " " + outputFolder;
+        Process proc = Runtime.getRuntime().exec(cmd);
+        proc.waitFor();
+    }
+
+    //璇诲彇鏂囦欢澶逛笅鐨勬墍鏈夋枃浠讹紙涓嶈鍙栨枃浠跺す鍐呯殑鏂囦欢锛�
+    public static List getFileNameWithOutDirectory(String filePath) {
+        File folder = new File(filePath); // 鏂囦欢澶硅矾寰�
+        List nameList=new ArrayList();
+        File[] listOfFiles = folder.listFiles();
+        if (listOfFiles != null) {
+            for (File file : listOfFiles) {
+                if (file.isFile()) {
+                    nameList.add(file.getName());
+                }
+            }
+        }
+        return nameList;
+    }
+
+    //private static void decompressRar(File file, String outputFolder) throws IOException, RarException {
+    //    Archive archive = new Archive(file);
+    //    FileHeader fileHeader = archive.nextFileHeader();
+    //    if (fileHeader != null) {
+    //        while (fileHeader != null) {
+    //            if (fileHeader.isDirectory()) {
+    //                fileHeader = archive.nextFileHeader();
+    //                continue;
+    //            }
+    //            String tempFilePath = fileHeader.getFileName();
+    //            File out = new File(outputFolder + File.separator + tempFilePath);
+    //            if (!out.exists()) {
+    //                if (!out.getParentFile().exists()) {
+    //                    out.getParentFile().mkdirs();
+    //                }
+    //                out.createNewFile();
+    //            }
+    //            FileOutputStream os = new FileOutputStream(out);
+    //            archive.extractFile(fileHeader, os);
+    //            os.close();
+    //            fileHeader = archive.nextFileHeader();
+    //        }
+    //    }
+    //    archive.close();
+    //
+    //}
+
+    public static void main(String[] args) {
+        //File file = new File("C:\\Users\\29550\\Desktop\\AppScan10.rar");
+        File file = new File("C:\\code\\web\\CadDrawManager\\target\\doc_file\\decompress\\doc_file@product@FBS-9600-GDPDX-XS1-DC220V-JH@standard@3@3++.rar");
+        if(file.exists()) {
+            boolean delete = file.delete();
+            System.out.println(delete);
+        }
+    }
 }

--
Gitblit v1.9.1