diff --git a/src/main/java/myutil/AIInterface.java b/src/main/java/myutil/AIInterface.java index ab580b30bac39119f08e9ccdc2019bdce3cf722b..147fe1a2379abbd15b385d79a0519a72ce9af963 100644 --- a/src/main/java/myutil/AIInterface.java +++ b/src/main/java/myutil/AIInterface.java @@ -46,6 +46,7 @@ import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; +import org.apache.batik.anim.timing.Trace; import org.json.JSONObject; import org.json.JSONArray; @@ -142,10 +143,11 @@ public class AIInterface { mainObject.put("messages", array); // Sending JSON - //TraceManager.addDev("Sending: " + mainObject.toString()); + TraceManager.addDev("Sending: " + mainObject.toString()); sendJSON(mainObject); StringBuilder sb = getAnswer(); + TraceManager.addDev("Got answer"); JSONObject answerObject = new JSONObject(sb.toString()); JSONArray answerArray = answerObject.getJSONArray("choices"); JSONObject answerText = answerArray.getJSONObject(0); diff --git a/src/main/java/myutil/GraphicLib.java b/src/main/java/myutil/GraphicLib.java index 372f65e54b9e7a9fc2cee2ae6913d3f01ab3e2e6..bee2e49198e48201f2410f08ce60b4600ad37cc3 100644 --- a/src/main/java/myutil/GraphicLib.java +++ b/src/main/java/myutil/GraphicLib.java @@ -40,6 +40,10 @@ package myutil; import javax.swing.*; +import javax.swing.text.AttributeSet; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.StyleConstants; +import javax.swing.text.StyleContext; import java.awt.*; import java.awt.geom.*; import java.util.*; @@ -824,5 +828,20 @@ public final class GraphicLib { return new Point( (int)(p1.getX() + percentage*deltaX), (int)(p1.getY() + percentage*deltaY) ); } + public static void appendToPane(JTextPane tp, String msg, Color c) { + StyleContext sc = StyleContext.getDefaultStyleContext(); + AttributeSet aset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, c); + + aset = sc.addAttribute(aset, + StyleConstants.FontFamily, + "Lucida Console"); + aset = sc.addAttribute(aset, StyleConstants.Alignment, StyleConstants.ALIGN_JUSTIFIED); + + int len = tp.getDocument().getLength(); + tp.setCaretPosition(len); + tp.setCharacterAttributes(aset, false); + tp.replaceSelection(msg); + } + } diff --git a/src/main/java/ui/GTURTLEModeling.java b/src/main/java/ui/GTURTLEModeling.java index 16015d8d2f67d1a730622cc6cddfb1d786f28c12..3dfb8111cc71beddf431d4530b7c82c0b399c03f 100644 --- a/src/main/java/ui/GTURTLEModeling.java +++ b/src/main/java/ui/GTURTLEModeling.java @@ -2062,7 +2062,7 @@ public class GTURTLEModeling { if (avatarspec != null) { AVATAR2SysMLV2 toS = new AVATAR2SysMLV2(avatarspec); StringBuffer sb = toS.generateSysMLV2Spec(true, true); - System.out.println("SysMLV2:\n" + sb.toString()); + TraceManager.addDev("SysMLV2:\n" + sb.toString()); return sb.toString(); } return null; diff --git a/src/main/java/ui/MainGUI.java b/src/main/java/ui/MainGUI.java index f7ead006f5a95503c04357eadc2bd3a83be658f5..1655085ff38e70551164ed26fb9a0f424e9d7cc8 100644 --- a/src/main/java/ui/MainGUI.java +++ b/src/main/java/ui/MainGUI.java @@ -5071,8 +5071,8 @@ public class MainGUI implements ActionListener, WindowListener, KeyListener, Per public void ai() { TraceManager.addDev("ai"); - JFrameAI frameAI = new JFrameAI("System engineering with AI"); - GraphicLib.centerOnParent(frameAI, 1000, 800); + JFrameAI frameAI = new JFrameAI("System engineering with AI", this); + GraphicLib.centerOnParent(frameAI, 1100, 900); frameAI.setVisible(true); } diff --git a/src/main/java/ui/TDiagramPanel.java b/src/main/java/ui/TDiagramPanel.java index 7bfa7caebd6555ac4c96c1aeb7cb114ab4afbcbc..458ed5593b36d2929f52207bd1dbf0d517f1505b 100644 --- a/src/main/java/ui/TDiagramPanel.java +++ b/src/main/java/ui/TDiagramPanel.java @@ -4447,6 +4447,10 @@ public abstract class TDiagramPanel extends JPanel implements GenericTree { } return nes; } + + public StringBuffer toSysMLV2Text() { + return new StringBuffer(); + } } diff --git a/src/main/java/ui/avatarrd/AvatarRDPanel.java b/src/main/java/ui/avatarrd/AvatarRDPanel.java index 08215396687da4ff2d31dc3f325d103f3e2fb3d5..7e2db5ae9adf3e3d5c22bf42424067080f45fa80 100755 --- a/src/main/java/ui/avatarrd/AvatarRDPanel.java +++ b/src/main/java/ui/avatarrd/AvatarRDPanel.java @@ -56,6 +56,8 @@ import java.util.*; * @version 1.0 20/04/2010 */ public class AvatarRDPanel extends TDiagramPanel implements TDPWithAttributes, NameChecker.SystemWithNamedElements { + private static String CR = "\n"; + public Vector validated, ignored; public AvatarRDPanel(MainGUI mgui, TToolBar _ttb) { @@ -172,6 +174,17 @@ public class AvatarRDPanel extends TDiagramPanel implements TDPWithAttributes, N return list; } + + public AvatarRDRequirement getReqWithName(String name) { + for(TGComponent tgc: componentList) { + if (tgc instanceof AvatarRDRequirement) { + if (tgc.getValue().compareTo(name) == 0) { + return (AvatarRDRequirement)tgc; + } + } + } + return null; + } /*public boolean isLinkedByVerifyTo(TGComponent tgc1, TGComponent tgc2) { ListIterator iterator = getComponentList().listIterator(); @@ -629,6 +642,48 @@ public class AvatarRDPanel extends TDiagramPanel implements TDPWithAttributes, N } } } + + public StringBuffer toSysMLV2Text() { + return toSysMLV2TextExcludeType(false); + } + + public StringBuffer toSysMLV2TextExcludeType(boolean excludeType) { + StringBuffer sb = new StringBuffer(); + for(TGComponent component: getComponentList()) { + if (component instanceof AvatarRDRequirement) { + AvatarRDRequirement req = (AvatarRDRequirement)component; + sb.append("requirement " + req.getValue() + CR); + sb.append("\ttext: " + req.getText() + CR); + if (!excludeType){ + sb.append("\ttype: " + req.getKind() + CR); + } + for(TGComponent relation: getComponentList()) { + if (relation instanceof TGConnector) { + if ( (relation instanceof AvatarRDRefineConnector) || (relation instanceof AvatarRDCompositionConnector) + || (relation instanceof AvatarRDDeriveConnector)) { + if (((TGConnector) relation).getTGConnectingPointP1().getFather() == req) { + if (((TGConnector)relation).getTGConnectingPointP2().getFather() instanceof AvatarRDRequirement) { + AvatarRDRequirement req2 = + (AvatarRDRequirement)(((TGConnector)relation).getTGConnectingPointP2().getFather()); + String relationS = "refine"; + if (relation instanceof AvatarRDCompositionConnector) { + relationS = "compose"; + } else if (relation instanceof AvatarRDDeriveConnector) { + relationS = "derive"; + } + sb.append("\t" + relationS + ": " + req2.getValue() + CR); + } + } + } + } + } + sb.append("end requirement " + CR + CR); + } + } + return sb; + } + + } diff --git a/src/main/java/ui/avatarrd/AvatarRDRequirement.java b/src/main/java/ui/avatarrd/AvatarRDRequirement.java index 9ae63000ef699d1b4ef6208b08bddd08b4fddc20..3e7f11a82e2f6ff040a42233a18be26b593a6215 100755 --- a/src/main/java/ui/avatarrd/AvatarRDRequirement.java +++ b/src/main/java/ui/avatarrd/AvatarRDRequirement.java @@ -1004,6 +1004,10 @@ public class AvatarRDRequirement extends TGCScalableWithInternalComponent implem return kind; } + public void setKind(String _kind) { + kind = _kind; + } + public String getViolatedAction() { return violatedAction; } diff --git a/src/main/java/ui/window/JDialogRequirement.java b/src/main/java/ui/window/JDialogRequirement.java index 1dfd2c4d741c9985a6ae1bba61b2da6e911c1a52..8aeb5cb5479db907af337eb20b69cd4cf56f6214 100644 --- a/src/main/java/ui/window/JDialogRequirement.java +++ b/src/main/java/ui/window/JDialogRequirement.java @@ -41,6 +41,7 @@ package ui.window; +import myutil.TraceManager; import ui.util.IconManager; import javax.swing.*; @@ -49,6 +50,7 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.LinkedList; +import java.util.Locale; //import javax.swing.event.*; //import java.util.*; @@ -63,7 +65,9 @@ import java.util.LinkedList; */ public class JDialogRequirement extends JDialogBase implements ActionListener { - public static String[] kinds = {"Functional", "Non-functional", "Performance", "Privacy", "Confidentiality", "Non-repudiation", "Controlled access (authorization)", "Availability", "Immunity", "Integrity", "Data origin authenticity", "Freshness", "Business", "Stakeholder need", "Other"}; + public static String[] kinds = {"Safety", "Functional", "Non-functional", "Performance", "Privacy", "Confidentiality", "Non-repudiation", + "Controlled access (authorization)", "Availability", "Immunity", "Integrity", "Data origin authenticity", "Freshness", "Business", + "Stakeholder need", "Other"}; private boolean regularClose; @@ -387,5 +391,18 @@ public class JDialogRequirement extends JDialogBase implements ActionListener { public String getExtraAttributes() { return jtaAttributes.getText(); } + + public static String getKindFromString(String _kind) { + _kind = _kind.toLowerCase(); + for (int i=0; i<kinds.length; i++) { + //TraceManager.addDev("Comparing >" + kinds[i] + "< with >" + _kind + "<"); + if (kinds[i].toLowerCase().compareTo(_kind) ==0 ) { + TraceManager.addDev("ok"); + return kinds[i]; + } + } + + return null; + } } diff --git a/src/main/java/ui/window/JFrameAI.java b/src/main/java/ui/window/JFrameAI.java index b9a24c0768b7a163ddab48de997a78e62db251eb..19fc86269987e07df691dfdc68d582aab8ec4087 100644 --- a/src/main/java/ui/window/JFrameAI.java +++ b/src/main/java/ui/window/JFrameAI.java @@ -39,18 +39,22 @@ package ui.window; +import common.ConfigurationTTool; +import myutil.AIInterface; +import myutil.AIInterfaceException; +import myutil.GraphicLib; import myutil.TraceManager; -import ui.ColorManager; -import ui.TGCSysMLV2; +import ui.MainGUI; +import ui.TDiagramPanel; +import ui.TGComponent; +import ui.avatarrd.AvatarRDPanel; +import ui.avatarrd.AvatarRDRequirement; import ui.util.IconManager; import javax.swing.*; -import javax.swing.plaf.basic.BasicTabbedPaneUI; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; /** @@ -60,18 +64,36 @@ import java.awt.event.MouseEvent; * * @author Ludovic APVRILLE */ -public class JFrameAI extends JFrame implements ActionListener { +public class JFrameAI extends JFrame implements ActionListener, Runnable { - private String [] POSSIBLE_ACTIONS = {"Chat", "Classify requirements"}; - private JTextArea question, answer, console; - protected JComboBox<String> listOfPossibleActions; + private static int KIND_CLASSIFY_REQUIREMENT = 1; + + private static String[] POSSIBLE_ACTIONS = {"Chat", "Classify requirements"}; + protected JComboBox<String> listOfPossibleActions; + private String QUESTION_CLASSIFY_REQ = "I would like to identify the \"type\" attribute, i.e. the classification, " + + "of the following requirements. Could you give me a correct type among: safety, security, functional, " + + "non-functional, performance, business, stakeholder need. if the main category is security, you can use the " + + "following sub categories: privacy, confidentiality, non-repudiation, controlled access, availability," + + "immunity, data origin authenticity, freshness. Use the following format for the answer:" + + " - Requirement name: classification\n"; + private MainGUI mgui; + private JTextPane question, answer, console; + private String automatedAnswer; + private TDiagramPanel previousTDP; + private int previousKind; + private String lastChatAnswer; + + private AIInterface aiinterface; + + private boolean go = false; private JButton buttonClose, buttonStart, buttonApplyResponse; - public JFrameAI(String title) { + public JFrameAI(String title, MainGUI _mgui) { super(title); + mgui = _mgui; makeComponents(); } @@ -90,6 +112,7 @@ public class JFrameAI extends JFrame implements ActionListener { listOfPossibleActions = new JComboBox<>(POSSIBLE_ACTIONS); panelTop.add(listOfPossibleActions, BorderLayout.CENTER); + listOfPossibleActions.addActionListener(this); framePanel.add(panelTop, BorderLayout.NORTH); @@ -105,26 +128,33 @@ public class JFrameAI extends JFrame implements ActionListener { JPanel questionPanel = new JPanel(); questionPanel.setBorder(new javax.swing.border.TitledBorder("Question")); questionPanel.setPreferredSize(new Dimension(450, 550)); - question = new JTextArea(); - question.setPreferredSize(new Dimension(400, 500)); + question = new JTextPane(); + //question.setPreferredSize(new Dimension(400, 500)); + setOptionsJTextPane(question, true); JScrollPane scrollPane = new JScrollPane(question); + scrollPane.setPreferredSize(new Dimension(420, 500)); + scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); questionPanel.add(scrollPane, BorderLayout.CENTER); JPanel answerPanel = new JPanel(); answerPanel.setBorder(new javax.swing.border.TitledBorder("Answer")); - answerPanel.setPreferredSize(new Dimension(450, 550)); - answer = new JTextArea(); - answer.setPreferredSize(new Dimension(400, 500)); - setOptionsJTextArea(answer, true); + answerPanel.setPreferredSize(new Dimension(550, 550)); + answer = new JTextPane(); + //answer.setPreferredSize(new Dimension(400, 500)); + //setOptionsJTextPane(answer, false); scrollPane = new JScrollPane(answer); + scrollPane.setPreferredSize(new Dimension(510, 500)); + scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); answerPanel.add(scrollPane, BorderLayout.CENTER); JPanel consolePanel = new JPanel(); consolePanel.setBorder(new javax.swing.border.TitledBorder("Console")); - console = new JTextArea(); - console.setPreferredSize(new Dimension(900, 150)); - addToConsole("Select options and click on \"start\""); + console = new JTextPane(); + //console.setPreferredSize(new Dimension(900, 150)); + inform("Select options and click on \"start\"\n"); scrollPane = new JScrollPane(console); + scrollPane.setPreferredSize(new Dimension(900, 150)); + scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); consolePanel.add(scrollPane, BorderLayout.CENTER); JPanel middlePanel = new JPanel(new BorderLayout()); @@ -137,17 +167,16 @@ public class JFrameAI extends JFrame implements ActionListener { framePanel.add(middlePanel, BorderLayout.CENTER); - // Lower panel - buttonClose = new JButton("Close", IconManager.imgic26); + buttonClose = new JButton("Close", IconManager.imgic27); buttonClose.addActionListener(this); buttonStart = new JButton("Start", IconManager.imgic53); buttonStart.addActionListener(this); - buttonApplyResponse = new JButton("Apply response", IconManager.imgic25); - buttonStart.addActionListener(this); + buttonApplyResponse = new JButton("Apply response", IconManager.imgic75); + buttonApplyResponse.addActionListener(this); JPanel lowPart = new JPanel(new BorderLayout()); JPanel jp = new JPanel(); @@ -164,7 +193,6 @@ public class JFrameAI extends JFrame implements ActionListener { } - public void actionPerformed(ActionEvent evt) { TraceManager.addDev("Action performed"); @@ -172,9 +200,12 @@ public class JFrameAI extends JFrame implements ActionListener { if (evt.getSource() == buttonClose) { close(); } else if (evt.getSource() == buttonStart) { + TraceManager.addDev("start!"); start(); } else if (evt.getSource() == buttonApplyResponse) { applyResponse(); + } else if (evt.getSource() == listOfPossibleActions) { + enableDisableActions(); } } @@ -184,36 +215,192 @@ public class JFrameAI extends JFrame implements ActionListener { } private void start() { - + runChat(); } private void applyResponse() { + if (previousKind == 1) { + applyRequirementClassification(); + } + } + + private void applyRequirementClassification() { + if (previousTDP == null) { + error("No diagram has been selected\n"); + return; + } + + if (!(previousTDP instanceof AvatarRDPanel)) { + error("Wrong diagram has been selected\n"); + return; + } + + AvatarRDPanel rdpanel = (AvatarRDPanel) previousTDP; + + inform("Enhancing requirement diagram with ai answer, please wait\n"); + + for (TGComponent tgc : rdpanel.getAllRequirements()) { + AvatarRDRequirement req = (AvatarRDRequirement) tgc; + String query = req.getValue() + ":"; + int index = automatedAnswer.indexOf(query); + if (index != -1) { + String kind = automatedAnswer.substring((index + query.length()), automatedAnswer.length()).trim(); + //TraceManager.addDev("Kind=" + kind); + int indexSpace = kind.indexOf("\n"); + int indexSpace1 = kind.indexOf(" "); + String kTmp; + if ((indexSpace1 > -1) || (indexSpace > -1)) { + if ((indexSpace1 > -1) && (indexSpace > -1)) { + indexSpace = Math.min(indexSpace, indexSpace1); + kTmp = kind.substring(0, indexSpace); + } else if (indexSpace1 > -1) { + kTmp = kind.substring(0, indexSpace1); + } else { + kTmp = kind.substring(0, indexSpace); + } + } else { + kTmp = kind; + } + + //TraceManager.addDev("Looking for req=" + req.getValue() + " to set kind to " + kTmp); + String k = JDialogRequirement.getKindFromString(kTmp); + if (k != null) { + req.setKind(k); + inform("\tRequirement " + req.getValue() + " kind was set to " + k + "\n"); + } else { + error("Unknown kind: " + k + "\n"); + } + } + } + + rdpanel.repaint(); + } private void enableDisableActions() { - + buttonApplyResponse.setEnabled(automatedAnswer != null); + buttonStart.setEnabled(!go); } - private void setOptionsJTextArea(JTextArea jta, boolean _isEditable) { + private void setOptionsJTextPane(JTextPane jta, boolean _isEditable) { jta.setEditable(_isEditable); jta.setMargin(new Insets(10, 10, 10, 10)); - jta.setTabSize(3); Font f = new Font("Courrier", Font.BOLD, 12); jta.setFont(f); } - private void addToConsole() { - console.append("Select options and click on \"start\""); + + private void runChat() { + Thread t = new Thread(this); + t.start(); + } + + public void run() { + go = true; + enableDisableActions(); + + if (makeAIInterface()) { + if (listOfPossibleActions.getSelectedIndex() == 0) { + simpleChat(); + } + + if (listOfPossibleActions.getSelectedIndex() == 1) { + classifyRequirements(); + } + + + } + + go = false; + enableDisableActions(); + } + + private void simpleChat() { + if (question.getText().trim().length() == 0) { + error("No question is provided. Aborting.\n\n"); + return; + } + + + inform("Simple chat is selected\n"); + TraceManager.addDev("Appending: " + question.getText().trim() + " to answer"); + GraphicLib.appendToPane(answer, "\nYou:" + question.getText().trim() + "\n", Color.blue); + + try { + GraphicLib.appendToPane(console, "Connecting, waiting for answer\n", Color.blue); + lastChatAnswer = aiinterface.chat(question.getText().trim(), true, true); + } catch (AIInterfaceException aiie) { + error(aiie.getMessage()); + return; + } + inform("Got answer from ai. All done.\n\n"); + GraphicLib.appendToPane(answer, "\nAI:" + lastChatAnswer + "\n", Color.red); + question.setText(""); + } + private void classifyRequirements() { + inform("Classifying requirements is selected\n"); + TDiagramPanel tdp = mgui.getCurrentTDiagramPanel(); + if (!(tdp instanceof AvatarRDPanel)) { + error("A requirement diagram must be selected first"); + return; + } + + String s = ((AvatarRDPanel) tdp).toSysMLV2TextExcludeType(true).toString(); + if (s.length() == 0) { + error("Empty requirement diagram. Aborting"); + return; + } + TraceManager.addDev("Appending: " + s.trim() + " to answer"); + String question = "\nTTool:" + QUESTION_CLASSIFY_REQ + "\n" + s.trim() + "\n"; + makeQuestion(question, KIND_CLASSIFY_REQUIREMENT, tdp); + } + private void makeQuestion(String _question, int _kind, TDiagramPanel _tdp) { + GraphicLib.appendToPane(answer, _question, Color.blue); + + try { + GraphicLib.appendToPane(console, "Connecting, waiting for answer\n", Color.blue); + automatedAnswer = aiinterface.chat(_question, true, true); + previousKind = _kind; + previousTDP = _tdp; + } catch (AIInterfaceException aiie) { + error(aiie.getMessage()); + return; + } + inform("Got answer from ai. All done.\n\n"); + GraphicLib.appendToPane(answer, "\nAI:" + automatedAnswer + "\n", Color.red); + } + private boolean makeAIInterface() { + if (aiinterface == null) { + String key = ConfigurationTTool.OPENAIKey; + if (key == null) { + error("No key has been set. Aborting.\n"); + return false; + } else { + TraceManager.addDev("Setting key: " + key); + aiinterface = new AIInterface(); + aiinterface.setURL(AIInterface.URL_OPENAI_COMPLETION); + aiinterface.setAIModel(AIInterface.MODEL_GPT_35); + aiinterface.setKey(key); + } + } + return true; + } + private void error(String text) { + GraphicLib.appendToPane(console, text, Color.red); + } + private void inform(String text) { + GraphicLib.appendToPane(console, text, Color.blue); + } -} // Class +} // Class \ No newline at end of file diff --git a/src/main/resources/ui/util/ia.gif b/src/main/resources/ui/util/ia.gif index d91486d77ef507a3bb45d151746199222b214c3a..7821666e5fd03bff4341dbbb8b4672c86a79845a 100644 Binary files a/src/main/resources/ui/util/ia.gif and b/src/main/resources/ui/util/ia.gif differ