diff --git a/build.gradle b/build.gradle index 5d5282b..e860137 100644 --- a/build.gradle +++ b/build.gradle @@ -59,7 +59,6 @@ dependencies { implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-hibernate5' implementation group: 'com.h2database', name:'h2' implementation group: 'commons-io', name: 'commons-io', version: '2.6' - implementation group: 'net.sourceforge.htmlunit', name: 'htmlunit', version: '2.35.0' implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.12.0' implementation group: 'io.springfox', name: 'springfox-boot-starter', version: '3.0.0' diff --git a/src/main/java/ru/ulstu/fc/config/MvcConfiguration.java b/src/main/java/ru/ulstu/fc/config/MvcConfiguration.java index 1203d38..cf79a53 100644 --- a/src/main/java/ru/ulstu/fc/config/MvcConfiguration.java +++ b/src/main/java/ru/ulstu/fc/config/MvcConfiguration.java @@ -14,6 +14,9 @@ import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.i18n.CookieLocaleResolver; import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; @Configuration public class MvcConfiguration implements WebMvcConfigurer { @@ -41,6 +44,14 @@ public class MvcConfiguration implements WebMvcConfigurer { return localeInterceptor; } + @Bean + public Docket api() { + return new Docket(DocumentationType.SWAGGER_2) + .select() + .apis(RequestHandlerSelectors.basePackage("ru.ulstu")) + .build(); + } + @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(localeInterceptor()); diff --git a/src/main/java/ru/ulstu/fc/rule/controller/InferenceMvcController.java b/src/main/java/ru/ulstu/fc/rule/controller/InferenceMvcController.java index 7267da1..b18e7af 100644 --- a/src/main/java/ru/ulstu/fc/rule/controller/InferenceMvcController.java +++ b/src/main/java/ru/ulstu/fc/rule/controller/InferenceMvcController.java @@ -9,11 +9,14 @@ import org.springframework.web.bind.annotation.RequestMethod; import ru.ulstu.fc.rule.model.Antecedent; import ru.ulstu.fc.rule.model.InferenceForm; import ru.ulstu.fc.rule.service.FuzzyInferenceService; +import springfox.documentation.annotations.ApiIgnore; import java.util.Arrays; import java.util.List; +import java.util.Map; @Controller +@ApiIgnore public class InferenceMvcController { private final FuzzyInferenceService fuzzyInferenceService; @@ -34,21 +37,24 @@ public class InferenceMvcController { model.addAttribute("ageAntecedents", getAgeAntecedents()); model.addAttribute("incomeAntecedents", getIncomeAntecedents()); model.addAttribute("inferenceForm", inferenceForm); - model.addAttribute("response", fuzzyInferenceService.getFuzzyInference()); + model.addAttribute("response", fuzzyInferenceService.getFuzzyInference( + Map.of("возраст", Double.valueOf(inferenceForm.getAgeAntecedent()), + "доход", Double.valueOf(inferenceForm.getIncomeAntecedent()) + ))); return "index"; } private List getAgeAntecedents() { return Arrays.asList( - new Antecedent("молодой", "young"), - new Antecedent("средний", "average"), - new Antecedent("старый", "old")); + new Antecedent("молодой", "30"), + new Antecedent("средний", "45"), + new Antecedent("старый", "60")); } private List getIncomeAntecedents() { return Arrays.asList( - new Antecedent("малый", "small"), - new Antecedent("средний", "average"), - new Antecedent("высокий", "high")); + new Antecedent("небольшой", "20000"), + new Antecedent("средний", "90000"), + new Antecedent("высокий", "200000")); } } diff --git a/src/main/java/ru/ulstu/fc/rule/controller/InferenceRestController.java b/src/main/java/ru/ulstu/fc/rule/controller/InferenceRestController.java new file mode 100644 index 0000000..0f3fb7e --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/controller/InferenceRestController.java @@ -0,0 +1,29 @@ +package ru.ulstu.fc.rule.controller; + +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import ru.ulstu.fc.rule.model.InferenceData; +import ru.ulstu.fc.rule.model.OutputValue; +import ru.ulstu.fc.rule.service.FuzzyInferenceService; + +import java.util.List; + +@RestController +@RequestMapping("rest") +public class InferenceRestController { + private final FuzzyInferenceService fuzzyInferenceService; + + public InferenceRestController(FuzzyInferenceService fuzzyInferenceService) { + this.fuzzyInferenceService = fuzzyInferenceService; + } + + @RequestMapping(value = "get-inference", method = RequestMethod.POST) + public List getInference(@RequestBody InferenceData inferenceData) { + return fuzzyInferenceService.getFuzzyInference(inferenceData.getRules(), + inferenceData.getValues(), + inferenceData.getInputVariables(), + inferenceData.getOutputVariable()); + } +} diff --git a/src/main/java/ru/ulstu/fc/rule/model/InferenceData.java b/src/main/java/ru/ulstu/fc/rule/model/InferenceData.java new file mode 100644 index 0000000..74e7ba9 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/model/InferenceData.java @@ -0,0 +1,43 @@ +package ru.ulstu.fc.rule.model; + +import java.util.List; +import java.util.Map; + +public class InferenceData { + private List rules; + private Map values; + private List inputVariables; + private Variable outputVariable; + + public List getRules() { + return rules; + } + + public void setRules(List rules) { + this.rules = rules; + } + + public Map getValues() { + return values; + } + + public void setValues(Map values) { + this.values = values; + } + + public List getInputVariables() { + return inputVariables; + } + + public void setInputVariables(List inputVariables) { + this.inputVariables = inputVariables; + } + + public Variable getOutputVariable() { + return outputVariable; + } + + public void setOutputVariable(Variable outputVariable) { + this.outputVariable = outputVariable; + } +} diff --git a/src/main/java/ru/ulstu/fc/rule/model/OutputValue.java b/src/main/java/ru/ulstu/fc/rule/model/OutputValue.java new file mode 100644 index 0000000..5a177e9 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/model/OutputValue.java @@ -0,0 +1,27 @@ +package ru.ulstu.fc.rule.model; + +public class OutputValue { + private String fuzzyTerm; + private Double degree; + + public OutputValue(String fuzzyTerm, Double degree) { + this.fuzzyTerm = fuzzyTerm; + this.degree = degree; + } + + public String getFuzzyTerm() { + return fuzzyTerm; + } + + public void setFuzzyTerm(String fuzzyTerm) { + this.fuzzyTerm = fuzzyTerm; + } + + public Double getDegree() { + return degree; + } + + public void setDegree(Double degree) { + this.degree = degree; + } +} diff --git a/src/main/java/ru/ulstu/fc/rule/model/VariableValue.java b/src/main/java/ru/ulstu/fc/rule/model/VariableValue.java new file mode 100644 index 0000000..9bc9550 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/model/VariableValue.java @@ -0,0 +1,30 @@ +package ru.ulstu.fc.rule.model; + +public class VariableValue { + private String fuzzyTerm; + private Double value; + + public VariableValue() { + } + + public VariableValue(String fuzzyTerm, Double value) { + this.fuzzyTerm = fuzzyTerm; + this.value = value; + } + + public String getFuzzyTerm() { + return fuzzyTerm; + } + + public void setFuzzyTerm(String fuzzyTerm) { + this.fuzzyTerm = fuzzyTerm; + } + + public Double getValue() { + return value; + } + + public void setValue(Double value) { + this.value = value; + } +} diff --git a/src/main/java/ru/ulstu/fc/rule/service/FuzzyInferenceService.java b/src/main/java/ru/ulstu/fc/rule/service/FuzzyInferenceService.java index 231b644..6082ebe 100644 --- a/src/main/java/ru/ulstu/fc/rule/service/FuzzyInferenceService.java +++ b/src/main/java/ru/ulstu/fc/rule/service/FuzzyInferenceService.java @@ -2,24 +2,24 @@ package ru.ulstu.fc.rule.service; import com.fuzzylite.Engine; import com.fuzzylite.activation.General; -import com.fuzzylite.defuzzifier.Centroid; +import com.fuzzylite.defuzzifier.WeightedAverage; import com.fuzzylite.norm.s.Maximum; import com.fuzzylite.norm.t.AlgebraicProduct; import com.fuzzylite.norm.t.Minimum; import com.fuzzylite.rule.Rule; import com.fuzzylite.rule.RuleBlock; -import com.fuzzylite.term.Activated; import com.fuzzylite.term.Triangle; import com.fuzzylite.variable.InputVariable; import com.fuzzylite.variable.OutputVariable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; +import ru.ulstu.fc.rule.model.OutputValue; +import ru.ulstu.fc.rule.model.Variable; +import ru.ulstu.fc.rule.model.VariableValue; -import java.util.AbstractMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.stream.Collectors; @Service @@ -32,69 +32,52 @@ public class FuzzyInferenceService { private final static String NO_RESULT = "Нет результата"; private final Engine fuzzyEngine; - private Map>> inputFuzzyTerms = Map.of( - "возраст", - List.of( - new AbstractMap.SimpleEntry("молодой", 35), - new AbstractMap.SimpleEntry("средний", 60), - new AbstractMap.SimpleEntry("старый", 100)), - "доход", - List.of( - new AbstractMap.SimpleEntry("небольшой", 35000), - new AbstractMap.SimpleEntry("средний", 100000), - new AbstractMap.SimpleEntry("высокий", 500000))); - - private Map>> outputFuzzyTerms = Map.of( - "кредит", List.of(new AbstractMap.SimpleEntry("небольшой", 20000), - new AbstractMap.SimpleEntry("средний", 100000), - new AbstractMap.SimpleEntry("большой", 1000000))); - - public FuzzyInferenceService(Engine fuzzyEngine) { - this.fuzzyEngine = fuzzyEngine; - } - private List getDemoRules() { return List.of( String.format(RULE_TEMPLATE, "возраст", "молодой", "доход", "высокий", "большой"), String.format(RULE_TEMPLATE, "возраст", "средний", "доход", "высокий", "средний"), - String.format(RULE_TEMPLATE, "возраст", "старый", "доход", "высокий", "средний") + String.format(RULE_TEMPLATE, "возраст", "старый", "доход", "высокий", "средний"), + String.format(RULE_TEMPLATE, "возраст", "старый", "доход", "небольшой", "небольшой"), + String.format(RULE_TEMPLATE, "возраст", "молодой", "доход", "небольшой", "небольшой") ); } - private List getInputVariables() { - return List.of(getInputVariable("возраст", inputFuzzyTerms.get("возраст")), - getInputVariable("доход", inputFuzzyTerms.get("доход"))); - } - - private InputVariable getInputVariable(String name, List> terms) { + private InputVariable getInputVariable(Variable variable) { final InputVariable input = new InputVariable(); - input.setName(name); + input.setName(variable.getName()); input.setDescription(""); - input.setRange(0, terms.get(terms.size() - 1).getValue()); + input.setRange(0, variable.getValues().get(variable.getValues().size() - 1).getValue()); input.setEnabled(true); input.setLockValueInRange(false); double prev = 0; - for (int i = 0; i < terms.size(); i++) { - Triangle term = new Triangle(terms.get(i).getKey(), prev, terms.get(i).getValue()); + for (int i = 0; i < variable.getValues().size(); i++) { + Triangle term = new Triangle(variable.getValues().get(i).getFuzzyTerm(), + prev, + variable.getValues().get(i).getValue(), + variable.getValues().get(i).getValue() + variable.getValues().get(i).getValue() - prev); prev = term.getVertexB(); input.addTerm(term); } return input; } - private > OutputVariable getOutputVariable(String name, List> terms) { + private > OutputVariable getOutputVariable(Variable variable) { final OutputVariable output = new OutputVariable(); - output.setName(name); + output.setName(variable.getName()); output.setDescription(""); - output.setRange(0, terms.get(terms.size() - 1).getValue()); + output.setRange(0, variable.getValues().get(variable.getValues().size() - 1).getValue()); output.setEnabled(true); output.setAggregation(new Maximum()); - output.setDefuzzifier(new Centroid(terms.get(terms.size() - 1).getValue())); + output.setDefuzzifier(new WeightedAverage()); output.setDefaultValue(Double.NaN); output.setLockValueInRange(false); double prev = 0; - for (int i = 0; i < terms.size(); i++) { - Triangle term = new Triangle(terms.get(i).getKey(), prev, terms.get(i).getValue()); + for (int i = 0; i < variable.getValues().size(); i++) { + Triangle term = new Triangle( + variable.getValues().get(i).getFuzzyTerm(), + prev, + variable.getValues().get(i).getValue(), + variable.getValues().get(i).getValue() + variable.getValues().get(i).getValue() - prev); prev = term.getVertexB(); output.addTerm(term); } @@ -102,9 +85,11 @@ public class FuzzyInferenceService { } private RuleBlock getRuleBlock(Engine engine, - List rules) { - getInputVariables().forEach(engine::addInputVariable); - engine.addOutputVariable(getOutputVariable("кредит", outputFuzzyTerms.get("кредит"))); + List rules, + List inputVariables, + Variable outputVariable) { + inputVariables.stream().map(this::getInputVariable).forEach(engine::addInputVariable); + engine.addOutputVariable(getOutputVariable(outputVariable)); RuleBlock mamdani = new RuleBlock(); mamdani.setName("mamdani"); @@ -117,7 +102,7 @@ public class FuzzyInferenceService { return mamdani; } - private Map getConsequent(Engine engine, Map variableValues) { + private List getConsequent(Engine engine, Map variableValues) { OutputVariable outputVariable = engine.getOutputVariable(OUTPUT_VARIABLE_NAME); for (Map.Entry variableValue : variableValues.entrySet()) { InputVariable inputVariable = engine.getInputVariable(variableValue.getKey()); @@ -128,14 +113,39 @@ public class FuzzyInferenceService { LOG.info("Output: {}", outputVariable.getValue()); } return Double.isNaN(outputVariable.getValue()) - ? Map.of(NO_RESULT, 0.0) - : outputVariable.fuzzyOutput().getTerms().stream().collect(Collectors.toMap(t -> t.getTerm().getName(), Activated::getDegree)); + ? List.of(new OutputValue(NO_RESULT, 0.0)) + : outputVariable.fuzzyOutput() + .getTerms() + .stream() + .map(t -> new OutputValue(t.getTerm().getName(), t.getDegree())) + .collect(Collectors.toList()); } - public Map getFuzzyInference() { + public List getFuzzyInference(Map vals) { + return getFuzzyInference(getDemoRules(), vals, + List.of(new Variable("возраст", List.of( + new VariableValue("молодой", 35.0), + new VariableValue("средний", 60.0), + new VariableValue("старый", 100.0)) + ), + new Variable("доход", List.of( + new VariableValue("небольшой", 35000.0), + new VariableValue("средний", 100000.0), + new VariableValue("высокий", 500000.0)) + ) + ), + new Variable("кредит", List.of( + new VariableValue("небольшой", 20000.0), + new VariableValue("средний", 100000.0), + new VariableValue("большой", 1000000.0))) + ); + } + + public List getFuzzyInference(List rules, + Map values, + List inputVariables, + Variable outputVariable) { fuzzyEngine.getRuleBlocks().clear(); - fuzzyEngine.addRuleBlock(getRuleBlock(fuzzyEngine, getDemoRules())); - return getConsequent(fuzzyEngine, Map.of("возраст", 20.0, "доход", 250000.0)); + fuzzyEngine.addRuleBlock(getRuleBlock(fuzzyEngine, getDemoRules())); return getConsequent(engine, values); } - } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8764e4c..7c1eddc 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -18,4 +18,6 @@ spring.datasource.password=password spring.jpa.database-platform=org.hibernate.dialect.H2Dialect spring.h2.console.enabled=true spring.jpa.hibernate.ddl-auto=update +# swagger-ui custom path +springdoc.swagger-ui.path=/swagger-ui.html diff --git a/src/main/resources/templates/default.html b/src/main/resources/templates/default.html index a3b330d..eea7eff 100644 --- a/src/main/resources/templates/default.html +++ b/src/main/resources/templates/default.html @@ -30,6 +30,9 @@ + diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 48b474d..fbeb483 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -50,7 +50,12 @@ -
+
+
Размер кредита:
+
+
Степень принадлежности:
+
+