diff --git a/build.gradle b/build.gradle index d044239..09ddc81 100644 --- a/build.gradle +++ b/build.gradle @@ -46,8 +46,7 @@ dependencies { implementation group: 'org.webjars', name: 'jquery', version: '3.6.0' implementation group: 'org.webjars', name: 'bootstrap', version: '4.6.0' implementation group: 'org.webjars', name: 'bootstrap-select', version: '1.13.8' - implementation group: 'org.webjars', name: 'font-awesome', version: '4.7.0' - implementation group: 'org.webjars', name: 'highcharts', version: '7.0.0' + implementation 'org.webjars.npm:bootstrap-icons:1.11.3' implementation group: 'org.springframework.boot', name: 'spring-boot-starter-test' } diff --git a/src/main/java/ru/ulstu/fc/project/service/ProjectVariableService.java b/src/main/java/ru/ulstu/fc/project/service/ProjectVariableService.java index 5ec7194..ad8b281 100644 --- a/src/main/java/ru/ulstu/fc/project/service/ProjectVariableService.java +++ b/src/main/java/ru/ulstu/fc/project/service/ProjectVariableService.java @@ -24,4 +24,18 @@ public class ProjectVariableService { } return variableRepository.findByProject(projectService.getById(projectId)); } + + public List getInputByProjectId(Integer projectId) { + if (projectId == null || projectId == 0) { + return Collections.emptyList(); + } + return variableRepository.getInputByProject(projectService.getById(projectId)); + } + + public List getOutputByProjectId(Integer projectId) { + if (projectId == null || projectId == 0) { + return Collections.emptyList(); + } + return variableRepository.getOutputByProject(projectService.getById(projectId)); + } } diff --git a/src/main/java/ru/ulstu/fc/rule/controller/FuzzyRuleController.java b/src/main/java/ru/ulstu/fc/rule/controller/FuzzyRuleController.java index a19f79f..e13f1c6 100644 --- a/src/main/java/ru/ulstu/fc/rule/controller/FuzzyRuleController.java +++ b/src/main/java/ru/ulstu/fc/rule/controller/FuzzyRuleController.java @@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import ru.ulstu.fc.rule.model.FuzzyRuleForm; import ru.ulstu.fc.rule.model.FuzzyTerm; -import ru.ulstu.fc.rule.model.Variable; +import ru.ulstu.fc.rule.model.dto.VariableDto; import ru.ulstu.fc.rule.service.FuzzyRuleService; import ru.ulstu.fc.rule.service.FuzzyTermService; import ru.ulstu.fc.rule.service.VariableService; @@ -65,11 +65,26 @@ public class FuzzyRuleController { @ResponseBody @GetMapping("/getVariables/{projectId}") - public List getVariables(@PathVariable("projectId") Integer projectId, - final HttpServletResponse response) { + public List getVariables(@PathVariable("projectId") Integer projectId, + final HttpServletResponse response) { response.addHeader("Cache-Control", "max-age=60, must-revalidate, no-transform"); - //TODO: return DTO without terms - return variableService.getAllByProject(projectId); + return variableService.getAllDtoByProject(projectId); + } + + @ResponseBody + @GetMapping("/getInputVariables/{projectId}") + public List getInputVariables(@PathVariable("projectId") Integer projectId, + final HttpServletResponse response) { + response.addHeader("Cache-Control", "max-age=60, must-revalidate, no-transform"); + return variableService.getInputVariablesDtoByProject(projectId); + } + + @ResponseBody + @GetMapping("/getOutputVariables/{projectId}") + public List getOutputVariables(@PathVariable("projectId") Integer projectId, + final HttpServletResponse response) { + response.addHeader("Cache-Control", "max-age=60, must-revalidate, no-transform"); + return variableService.getOutputVariablesDtoByProject(projectId); } @ResponseBody diff --git a/src/main/java/ru/ulstu/fc/rule/controller/FuzzyRuleRestController.java b/src/main/java/ru/ulstu/fc/rule/controller/FuzzyRuleRestController.java index 03469d9..158e9ba 100644 --- a/src/main/java/ru/ulstu/fc/rule/controller/FuzzyRuleRestController.java +++ b/src/main/java/ru/ulstu/fc/rule/controller/FuzzyRuleRestController.java @@ -7,22 +7,34 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import ru.ulstu.fc.project.service.ProjectRulesService; import ru.ulstu.fc.rule.model.FuzzyRule; import ru.ulstu.fc.rule.model.FuzzyRuleForm; import ru.ulstu.fc.rule.service.FuzzyRuleService; +import java.util.List; + @RestController @RequestMapping("fuzzyRuleRest") public class FuzzyRuleRestController { private final FuzzyRuleService ruleService; + private final ProjectRulesService projectRulesService; - public FuzzyRuleRestController(FuzzyRuleService ruleService) { + public FuzzyRuleRestController(FuzzyRuleService ruleService, + ProjectRulesService projectRulesService) { this.ruleService = ruleService; + this.projectRulesService = projectRulesService; } - @GetMapping("/get/{projectId}/{ruleId}") - public FuzzyRule get(@PathVariable(value = "projectId") Integer projectId, - @PathVariable(value = "ruleId") Integer id) { + @GetMapping("/getAll/{projectId}") + public List getAll(@PathVariable(value = "projectId") Integer projectId) { + //TODO: return dto + return projectRulesService.getByProjectId(projectId); + } + + @GetMapping("/get/{ruleId}") + public FuzzyRule get(@PathVariable(value = "ruleId") Integer id) { + //TODO: return dto return ruleService.getById(id); } diff --git a/src/main/java/ru/ulstu/fc/rule/controller/InferenceRestController.java b/src/main/java/ru/ulstu/fc/rule/controller/InferenceRestController.java index dcb0a8f..b95cd74 100644 --- a/src/main/java/ru/ulstu/fc/rule/controller/InferenceRestController.java +++ b/src/main/java/ru/ulstu/fc/rule/controller/InferenceRestController.java @@ -6,6 +6,7 @@ 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.model.ProjectInferenceData; import ru.ulstu.fc.rule.service.FuzzyInferenceService; import java.util.List; @@ -24,6 +25,11 @@ public class InferenceRestController { return fuzzyInferenceService.getFuzzyInference(inferenceData.getRules(), inferenceData.getValues(), inferenceData.getInputVariables(), - inferenceData.getOutputVariable()); + inferenceData.getOutputVariables()); + } + + @RequestMapping(value = "getProjectInference", method = RequestMethod.POST) + public List getProjectInference(@RequestBody ProjectInferenceData projectInferenceData) { + return fuzzyInferenceService.getProjectFuzzyInference(projectInferenceData); } } diff --git a/src/main/java/ru/ulstu/fc/rule/model/InferenceData.java b/src/main/java/ru/ulstu/fc/rule/model/InferenceData.java index 74e7ba9..54e8011 100644 --- a/src/main/java/ru/ulstu/fc/rule/model/InferenceData.java +++ b/src/main/java/ru/ulstu/fc/rule/model/InferenceData.java @@ -7,7 +7,7 @@ public class InferenceData { private List rules; private Map values; private List inputVariables; - private Variable outputVariable; + private List outputVariables; public List getRules() { return rules; @@ -33,11 +33,11 @@ public class InferenceData { this.inputVariables = inputVariables; } - public Variable getOutputVariable() { - return outputVariable; + public List getOutputVariables() { + return outputVariables; } - public void setOutputVariable(Variable outputVariable) { - this.outputVariable = outputVariable; + public void setOutputVariable(List outputVariables) { + this.outputVariables = outputVariables; } } diff --git a/src/main/java/ru/ulstu/fc/rule/model/OutputValue.java b/src/main/java/ru/ulstu/fc/rule/model/OutputValue.java index 5a177e9..67d179d 100644 --- a/src/main/java/ru/ulstu/fc/rule/model/OutputValue.java +++ b/src/main/java/ru/ulstu/fc/rule/model/OutputValue.java @@ -1,10 +1,12 @@ package ru.ulstu.fc.rule.model; public class OutputValue { + private String variable; private String fuzzyTerm; private Double degree; - public OutputValue(String fuzzyTerm, Double degree) { + public OutputValue(String variable, String fuzzyTerm, Double degree) { + this.variable = variable; this.fuzzyTerm = fuzzyTerm; this.degree = degree; } @@ -24,4 +26,12 @@ public class OutputValue { public void setDegree(Double degree) { this.degree = degree; } + + public String getVariable() { + return variable; + } + + public void setVariable(String variable) { + this.variable = variable; + } } diff --git a/src/main/java/ru/ulstu/fc/rule/model/ProjectInferenceData.java b/src/main/java/ru/ulstu/fc/rule/model/ProjectInferenceData.java new file mode 100644 index 0000000..3518972 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/model/ProjectInferenceData.java @@ -0,0 +1,26 @@ +package ru.ulstu.fc.rule.model; + +import ru.ulstu.fc.rule.model.dto.VariableValueDto; + +import java.util.List; + +public class ProjectInferenceData { + private Integer projectId; + private List variableValues; + + public Integer getProjectId() { + return projectId; + } + + public void setProjectId(Integer projectId) { + this.projectId = projectId; + } + + public List getVariableValues() { + return variableValues; + } + + public void setVariableValues(List variableValues) { + this.variableValues = variableValues; + } +} diff --git a/src/main/java/ru/ulstu/fc/rule/model/dto/FuzzyTermDto.java b/src/main/java/ru/ulstu/fc/rule/model/dto/FuzzyTermDto.java new file mode 100644 index 0000000..85a3bf9 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/model/dto/FuzzyTermDto.java @@ -0,0 +1,42 @@ +package ru.ulstu.fc.rule.model.dto; + +import ru.ulstu.fc.rule.model.FuzzyTerm; + +public class FuzzyTermDto { + private Integer id; + private String description; + private Double crispValue; + + public FuzzyTermDto() { + } + + public FuzzyTermDto(FuzzyTerm fuzzyTerm) { + this.description = fuzzyTerm.getDescription(); + this.crispValue = fuzzyTerm.getCrispValue(); + this.id = fuzzyTerm.getId(); + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Double getCrispValue() { + return crispValue; + } + + public void setCrispValue(Double crispValue) { + this.crispValue = crispValue; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } +} diff --git a/src/main/java/ru/ulstu/fc/rule/model/dto/VariableDto.java b/src/main/java/ru/ulstu/fc/rule/model/dto/VariableDto.java new file mode 100644 index 0000000..541874a --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/model/dto/VariableDto.java @@ -0,0 +1,55 @@ +package ru.ulstu.fc.rule.model.dto; + +import ru.ulstu.fc.rule.model.Variable; + +import java.util.ArrayList; +import java.util.List; + +public class VariableDto { + private Integer id; + private String name; + private boolean input; + private List terms = new ArrayList<>(); + + public VariableDto() { + } + + public VariableDto(Variable variable) { + this.id = variable.getId(); + this.name = variable.getName(); + this.terms = variable.getFuzzyTerms().stream().map(FuzzyTermDto::new).toList(); + this.input = variable.isInput(); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getTerms() { + return terms; + } + + public void setTerms(List terms) { + this.terms = terms; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public boolean isInput() { + return input; + } + + public void setInput(boolean input) { + this.input = input; + } +} diff --git a/src/main/java/ru/ulstu/fc/rule/model/dto/VariableValueDto.java b/src/main/java/ru/ulstu/fc/rule/model/dto/VariableValueDto.java new file mode 100644 index 0000000..6984841 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/model/dto/VariableValueDto.java @@ -0,0 +1,22 @@ +package ru.ulstu.fc.rule.model.dto; + +public class VariableValueDto { + private String variableName; + private Double value; + + public String getVariableName() { + return variableName; + } + + public void setVariableName(String variableName) { + this.variableName = variableName; + } + + public Double getValue() { + return value; + } + + public void setValue(Double value) { + this.value = value; + } +} diff --git a/src/main/java/ru/ulstu/fc/rule/repository/VariableRepository.java b/src/main/java/ru/ulstu/fc/rule/repository/VariableRepository.java index 9499f09..881ce65 100644 --- a/src/main/java/ru/ulstu/fc/rule/repository/VariableRepository.java +++ b/src/main/java/ru/ulstu/fc/rule/repository/VariableRepository.java @@ -1,6 +1,8 @@ package ru.ulstu.fc.rule.repository; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import ru.ulstu.fc.project.model.Project; import ru.ulstu.fc.rule.model.Variable; @@ -11,4 +13,10 @@ public interface VariableRepository extends JpaRepository { List findByProject(Project project); List getByProject(Project project); + + @Query("SELECT v FROM Variable v WHERE v.project = :project AND v.input = true") + List getInputByProject(@Param("project") Project project); + + @Query("SELECT v FROM Variable v WHERE v.project = :project AND v.input = false") + List getOutputByProject(@Param("project") Project project); } 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 3812d83..c8d251c 100644 --- a/src/main/java/ru/ulstu/fc/rule/service/FuzzyInferenceService.java +++ b/src/main/java/ru/ulstu/fc/rule/service/FuzzyInferenceService.java @@ -14,10 +14,17 @@ import com.fuzzylite.variable.OutputVariable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; +import ru.ulstu.fc.project.service.ProjectRulesService; +import ru.ulstu.fc.project.service.ProjectService; +import ru.ulstu.fc.project.service.ProjectVariableService; +import ru.ulstu.fc.rule.model.FuzzyRule; import ru.ulstu.fc.rule.model.FuzzyTerm; import ru.ulstu.fc.rule.model.OutputValue; +import ru.ulstu.fc.rule.model.ProjectInferenceData; import ru.ulstu.fc.rule.model.Variable; +import ru.ulstu.fc.rule.model.dto.VariableValueDto; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -31,9 +38,17 @@ public class FuzzyInferenceService { + " is %s"; private final static String NO_RESULT = "Нет результата"; private final Engine fuzzyEngine; + private final ProjectService projectService; + private final FuzzyRuleService fuzzyRuleService; + private final ProjectRulesService projectRulesService; + private final ProjectVariableService projectVariableService; - public FuzzyInferenceService(Engine fuzzyEngine) { + public FuzzyInferenceService(Engine fuzzyEngine, ProjectService projectService, FuzzyRuleService fuzzyRuleService, ProjectRulesService projectRulesService, ProjectVariableService projectVariableService) { this.fuzzyEngine = fuzzyEngine; + this.projectService = projectService; + this.fuzzyRuleService = fuzzyRuleService; + this.projectRulesService = projectRulesService; + this.projectVariableService = projectVariableService; } private List getDemoRules() { @@ -48,6 +63,7 @@ public class FuzzyInferenceService { final InputVariable input = new InputVariable(); input.setName(variable.getName()); input.setDescription(""); + variable.getFuzzyTerms().sort(Comparator.comparing(FuzzyTerm::getCrispValue)); input.setRange(0, variable.getFuzzyTerms().get(variable.getFuzzyTerms().size() - 1).getCrispValue()); input.setEnabled(true); input.setLockValueInRange(false); @@ -89,9 +105,9 @@ public class FuzzyInferenceService { private RuleBlock getRuleBlock(Engine engine, List rules, List inputVariables, - Variable outputVariable) { + List outputVariables) { inputVariables.stream().map(this::getInputVariable).forEach(engine::addInputVariable); - engine.addOutputVariable(getOutputVariable(outputVariable)); + outputVariables.stream().map(this::getOutputVariable).forEach(engine::addOutputVariable); RuleBlock mamdani = new RuleBlock(); mamdani.setName("mamdani"); @@ -106,22 +122,21 @@ public class FuzzyInferenceService { } private List getConsequent(Engine engine, Map variableValues) { - OutputVariable outputVariable = engine.getOutputVariable(OUTPUT_VARIABLE_NAME); + List outputVariables = engine.getOutputVariables(); for (Map.Entry variableValue : variableValues.entrySet()) { InputVariable inputVariable = engine.getInputVariable(variableValue.getKey()); inputVariable.setValue(variableValue.getValue()); } engine.process(); - if (outputVariable != null) { - LOG.info("Output: {}", outputVariable.getValue()); - } - return Double.isNaN(outputVariable.getValue()) - ? List.of(new OutputValue(NO_RESULT, 0.0)) - : outputVariable.fuzzyOutput() - .getTerms() + + return outputVariables .stream() - .map(t -> new OutputValue(t.getTerm().getName(), t.getDegree())) - .collect(Collectors.toList()); + .filter(v -> !Double.isNaN(v.getValue())) + .map(OutputVariable::fuzzyOutput) + .map(a -> new OutputValue(a.getName(), + a.getTerms().getFirst().getTerm().getName(), + a.getTerms().getFirst().getDegree())) + .toList(); } public List getFuzzyInference(Map vals) { @@ -137,19 +152,40 @@ public class FuzzyInferenceService { new FuzzyTerm("высокий", 500000.0)) ) ), - new Variable("кредит", List.of( + List.of(new Variable("кредит", List.of( new FuzzyTerm("небольшой", 20000.0), new FuzzyTerm("средний", 100000.0), - new FuzzyTerm("большой", 1000000.0))) + new FuzzyTerm("большой", 1000000.0)))) ); } public List getFuzzyInference(List rules, Map values, List inputVariables, - Variable outputVariable) { + List outputVariables) { fuzzyEngine.getRuleBlocks().clear(); - fuzzyEngine.addRuleBlock(getRuleBlock(fuzzyEngine, rules, inputVariables, outputVariable)); + fuzzyEngine.getInputVariables().clear(); + fuzzyEngine.getOutputVariables().clear(); + fuzzyEngine.addRuleBlock(getRuleBlock(fuzzyEngine, rules, inputVariables, outputVariables)); return getConsequent(fuzzyEngine, values); } + + public List getProjectFuzzyInference(ProjectInferenceData projectInferenceData) { + List fuzzyRules = projectRulesService.getByProjectId(projectInferenceData.getProjectId()) + .stream() + .map(FuzzyRule::getContent) + .toList(); + Map variableValues = projectInferenceData.getVariableValues() + .stream() + .collect(Collectors.toMap( + VariableValueDto::getVariableName, + VariableValueDto::getValue)); + List inputVariables = projectVariableService.getInputByProjectId(projectInferenceData.getProjectId()); + List outputVariables = projectVariableService.getOutputByProjectId(projectInferenceData.getProjectId()); + + return getFuzzyInference(fuzzyRules, + variableValues, + inputVariables, + outputVariables); + } } diff --git a/src/main/java/ru/ulstu/fc/rule/service/VariableService.java b/src/main/java/ru/ulstu/fc/rule/service/VariableService.java index 732aebb..93e3f94 100644 --- a/src/main/java/ru/ulstu/fc/rule/service/VariableService.java +++ b/src/main/java/ru/ulstu/fc/rule/service/VariableService.java @@ -5,6 +5,7 @@ import ru.ulstu.fc.project.service.ProjectService; import ru.ulstu.fc.rule.model.FuzzyTerm; import ru.ulstu.fc.rule.model.Variable; import ru.ulstu.fc.rule.model.VariableForm; +import ru.ulstu.fc.rule.model.dto.VariableDto; import ru.ulstu.fc.rule.repository.VariableRepository; import java.util.List; @@ -69,7 +70,28 @@ public class VariableService { return variableRepository.getByProject(projectService.getById(projectId)); } + public List getAllDtoByProject(Integer projectId) { + return variableRepository.getByProject(projectService.getById(projectId)) + .stream() + .map(VariableDto::new) + .toList(); + } + public void checkIsCurrentUserVariableWithThrow(Variable variable) { projectService.checkIsCurrentUserProjectWithThrow(variable.getProject()); } + + public List getInputVariablesDtoByProject(Integer projectId) { + return variableRepository.getInputByProject(projectService.getById(projectId)) + .stream() + .map(VariableDto::new) + .toList(); + } + + public List getOutputVariablesDtoByProject(Integer projectId) { + return variableRepository.getOutputByProject(projectService.getById(projectId)) + .stream() + .map(VariableDto::new) + .toList(); + } } diff --git a/src/main/resources/public/js/fuzzyRule.js b/src/main/resources/public/js/fuzzyRule.js index e678cd9..1b3ce74 100644 --- a/src/main/resources/public/js/fuzzyRule.js +++ b/src/main/resources/public/js/fuzzyRule.js @@ -5,8 +5,7 @@ function getAntecedent(rule) { return withoutIf[1].trim().split('then')[0].trim(); } -// TODO: remove duplicate -function getAntecedentComponents(antecedent) { +function getRuleComponents(antecedent) { return antecedent.split('and').map((i) => i.trim()); } @@ -16,11 +15,6 @@ function getConsequent(rule) { return withoutIf[1].trim().split('then')[1].trim(); } -// TODO: remove duplicate -function getConsequentComponents(consequent) { - return consequent.split('and').map((i) => i.trim()); -} - // common function getVariable(variableComponents) { return variableComponents.split('is')[0].trim(); @@ -56,8 +50,6 @@ function showFeedbackMessage(message, type) { /* exported errorHandler */ function errorHandler(response, callBack, errorCallBack) { if (!isEmpty(response.error)) { - // TODO: add l10n - // showFeedbackMessage(response.error.code + ": " + response.error.message, MessageTypesEnum.DANGER); if (!isEmpty(errorCallBack)) { errorCallBack(response.data); } @@ -70,15 +62,12 @@ function errorHandler(response, callBack, errorCallBack) { } /* exported getFromRest */ -function getFromRest(url, callBack) { - $.ajax({ +async function getFromRest(url) { + return await $.ajax({ url: url, method: 'get', cache: true, - dataType: 'json', - success: function (response) { - errorHandler(response, callBack); - } + dataType: 'json' }); } @@ -120,60 +109,63 @@ function fillSelect(selectElement, values, selectedVal) { $(selectElement).append($("