mirror of
https://github.com/bancika/diy-layout-creator
synced 2026-11-10 17:22:09 +10:00
Cloud login improvements
This commit is contained in:
+4
-23
@@ -111,7 +111,7 @@ public class ChatbotService {
|
||||
if (netlists == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Components: ");
|
||||
|
||||
@@ -169,25 +169,6 @@ public class ChatbotService {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getInitialChatContents() {
|
||||
Random r = new Random();
|
||||
|
||||
String chatHtml =
|
||||
"<div class='system'>" + WELCOME_MESSAGE + "</div><br><br>\n" +
|
||||
"<div class='assistant'>I'm here to help you design, build, and troubleshoot your electronics projects using DIY Layout Creator (DIYLC). You can ask me questions about:</div><br>\n" +
|
||||
// "<ul>\n" +
|
||||
"\n" +
|
||||
"- <b>Electronics theory or concepts:</b> <i>e.g.,</i> '<span class='user'>" + electronicsQuestions[r.nextInt(
|
||||
electronicsQuestions.length)] + "</span>'<br>\n" +
|
||||
"- <b>DIYLC features and usage:</b> <i>e.g.,</i> '<span class='user'>" + diylcQuestions[r.nextInt(diylcQuestions.length)] + "</span>'<br>\n" +
|
||||
"- <b>Your current circuit project:</b> <i>e.g.,</i> '<span class='user'>" + circuitQuestions[r.nextInt(circuitQuestions.length)] + "</span>'<br>\n" +
|
||||
// "</ul><br>\n" +
|
||||
"<br>\n" +
|
||||
"<div class='assistant'>Feel free to ask your own question or use one of these examples to get started!</div><br>\n";
|
||||
|
||||
return chatHtml;
|
||||
}
|
||||
|
||||
public SubscriptionEntity getSubscriptionInfo() throws NotLoggedInException {
|
||||
if (!plugInPort.getCloudService().isLoggedIn())
|
||||
throw new NotLoggedInException();
|
||||
@@ -219,7 +200,7 @@ public class ChatbotService {
|
||||
public static final String USER = "user";
|
||||
public static final String TEMPORARY = "temporary";
|
||||
|
||||
String[] electronicsQuestions = {
|
||||
public static final String[] electronicsQuestions = {
|
||||
"What is Ohm's Law, and how do voltage, current, and resistance relate to each other?",
|
||||
"Why are resistors important in circuits, and what happens if the resistor value is incorrect?",
|
||||
"What is a capacitor, and how does it behave differently from a resistor?",
|
||||
@@ -247,7 +228,7 @@ public class ChatbotService {
|
||||
"How can you determine if components in your circuit are properly rated for the voltage and current they're experiencing?"
|
||||
};
|
||||
|
||||
String[] diylcQuestions = {
|
||||
public static final String[] diylcQuestions = {
|
||||
"How do I add a component to my layout in DIYLC?",
|
||||
"How can I quickly find a specific component in DIYLC?s component toolbox?",
|
||||
"How do I move or reposition components on the canvas in DIYLC?",
|
||||
@@ -270,7 +251,7 @@ public class ChatbotService {
|
||||
"What is the DIYLC cloud feature and how can I share or download projects using it?"
|
||||
};
|
||||
|
||||
String[] circuitQuestions = {
|
||||
public static final String[] circuitQuestions = {
|
||||
"What's the purpose of this specific resistor in my circuit?",
|
||||
"How do I correctly determine capacitor values for my power supply?",
|
||||
"How should I route wiring to reduce interference and noise?",
|
||||
|
||||
@@ -21,9 +21,8 @@
|
||||
*/
|
||||
package org.diylc.plugins.cloud.service;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.*;
|
||||
import java.lang.management.RuntimeMXBean;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.URL;
|
||||
@@ -33,10 +32,12 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.diylc.appframework.miscutils.ConfigurationManager;
|
||||
import java.lang.management.ManagementFactory;
|
||||
|
||||
import com.diyfever.httpproxy.PhpFlatProxy;
|
||||
import com.diyfever.httpproxy.ProxyFactory;
|
||||
|
||||
import org.diylc.appframework.miscutils.Utils;
|
||||
import org.diylc.common.EventType;
|
||||
import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.common.PropertyWrapper;
|
||||
@@ -47,6 +48,14 @@ import org.diylc.plugins.cloud.model.UserEntity;
|
||||
import org.diylc.presenter.ComparatorFactory;
|
||||
import org.diylc.presenter.ComponentProcessor;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
|
||||
/**
|
||||
* Contains all the back-end logic for using the cloud and manipulating projects on the cloud.
|
||||
*
|
||||
@@ -54,17 +63,21 @@ import org.diylc.presenter.ComponentProcessor;
|
||||
*/
|
||||
public class CloudService {
|
||||
|
||||
private static String USERNAME_KEY = "cloud.Username";
|
||||
private static String TOKEN_KEY = "cloud.token";
|
||||
|
||||
private static String ERROR = "Error";
|
||||
|
||||
private final static Logger LOG = Logger.getLogger(CloudService.class);
|
||||
private static final Logger LOG = Logger.getLogger(CloudService.class);
|
||||
private static final String MACHINE_ID_FILE = "machine.id";
|
||||
private static final String MACHINE_ID_KEY = "cloud.machineId";
|
||||
private static final String USERNAME_KEY = "cloud.Username";
|
||||
private static final String TOKEN_KEY = "cloud.token";
|
||||
private static final String ERROR = "Error";
|
||||
private static final Object SUCCESS = "Success";
|
||||
private static final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
private final IPlugInPort plugInPort;
|
||||
|
||||
private IServiceAPI service;
|
||||
private String serviceUrl;
|
||||
private UserEntity currentUser;
|
||||
private String currentToken;
|
||||
private String machineId;
|
||||
private String[] categories;
|
||||
|
||||
@@ -72,6 +85,22 @@ public class CloudService {
|
||||
|
||||
public CloudService(IPlugInPort plugInPort) {
|
||||
this.plugInPort = plugInPort;
|
||||
|
||||
serviceUrl = ConfigurationManager.getInstance().readString(IServiceAPI.URL_KEY,
|
||||
"http://www.diy-fever.com/diylc/api/v1");
|
||||
ProxyFactory factory = new ProxyFactory(new PhpFlatProxy());
|
||||
service = factory.createProxy(IServiceAPI.class, serviceUrl);
|
||||
|
||||
// Delay a test of logged in state to give it time to complete
|
||||
Timer timer = new Timer(1000, e -> {
|
||||
try {
|
||||
tryLogInWithToken();
|
||||
} catch (Exception ex) {
|
||||
LOG.error("Background login attempt failed", ex);
|
||||
}
|
||||
});
|
||||
timer.setRepeats(false); // Make it one-shot
|
||||
timer.start();
|
||||
}
|
||||
|
||||
private IServiceAPI getService() {
|
||||
@@ -107,7 +136,7 @@ public class CloudService {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean tryLogInWithToken() throws CloudException {
|
||||
private void tryLogInWithToken() throws CloudException {
|
||||
String username = ConfigurationManager.getInstance().readString(USERNAME_KEY, null);
|
||||
String token = ConfigurationManager.getInstance().readString(TOKEN_KEY, null);
|
||||
|
||||
@@ -121,22 +150,19 @@ public class CloudService {
|
||||
}
|
||||
if (res == null || res.equals(ERROR)) {
|
||||
LOG.info("Login failed");
|
||||
return false;
|
||||
} else {
|
||||
LOG.info("Login success");
|
||||
this.loggedIn = true;
|
||||
this.plugInPort.getMessageDispatcher().dispatchMessage(EventType.CLOUD_LOGGED_IN);
|
||||
return true;
|
||||
}
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void logOut() {
|
||||
LOG.info("Logged out");
|
||||
ConfigurationManager.getInstance().writeValue(TOKEN_KEY, null);
|
||||
this.plugInPort.getMessageDispatcher().dispatchMessage(EventType.CLOUD_LOGGED_OUT);
|
||||
this.loggedIn = false;
|
||||
this.plugInPort.getMessageDispatcher().dispatchMessage(EventType.CLOUD_LOGGED_OUT);
|
||||
}
|
||||
|
||||
public boolean isLoggedIn() {
|
||||
@@ -428,22 +454,67 @@ public class CloudService {
|
||||
|
||||
public String getMachineId() {
|
||||
if (machineId == null) {
|
||||
try {
|
||||
InetAddress ip = InetAddress.getLocalHost();
|
||||
|
||||
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
|
||||
|
||||
byte[] mac = network.getHardwareAddress();
|
||||
|
||||
StringBuilder sb = new StringBuilder(18);
|
||||
for (byte b : mac) {
|
||||
if (sb.length() > 0)
|
||||
sb.append(':');
|
||||
sb.append(String.format("%02x", b));
|
||||
if (Utils.isWindows()) {
|
||||
machineId = System.getenv("COMPUTERNAME");
|
||||
} else {
|
||||
try {
|
||||
Runtime r = Runtime.getRuntime();
|
||||
Process p = r.exec("uname -a");
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
|
||||
machineId = reader.readLine();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error getting machineId from uname", e);
|
||||
}
|
||||
}
|
||||
if (machineId == null) {
|
||||
try {
|
||||
// Generate a unique identifier based on hardware information
|
||||
List<String> identifiers = new ArrayList<>();
|
||||
|
||||
machineId = sb.toString();
|
||||
} catch (Exception e) {
|
||||
// Get MAC addresses
|
||||
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
|
||||
while (networkInterfaces.hasMoreElements()) {
|
||||
NetworkInterface networkInterface = networkInterfaces.nextElement();
|
||||
byte[] mac = networkInterface.getHardwareAddress();
|
||||
if (mac != null) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < mac.length; i++) {
|
||||
sb.append(String.format("%02X", mac[i]));
|
||||
}
|
||||
identifiers.add(sb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// Get hostname
|
||||
String hostname = InetAddress.getLocalHost().getHostName();
|
||||
identifiers.add(hostname);
|
||||
|
||||
// Sort to ensure consistent order
|
||||
Collections.sort(identifiers);
|
||||
|
||||
// Create a single string from all identifiers
|
||||
StringBuilder combined = new StringBuilder();
|
||||
for (String id : identifiers) {
|
||||
combined.append(id);
|
||||
}
|
||||
|
||||
// Create a hash of the combined string
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
byte[] hash = md.digest(combined.toString().getBytes());
|
||||
|
||||
// Convert to hex string
|
||||
StringBuilder hexString = new StringBuilder();
|
||||
for (byte b : hash) {
|
||||
hexString.append(String.format("%02x", b));
|
||||
}
|
||||
|
||||
machineId = hexString.toString();
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to generate machine ID", e);
|
||||
}
|
||||
}
|
||||
if (machineId == null) {
|
||||
machineId = "Generic";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,13 @@ import org.diylc.plugins.chatbot.model.SubscriptionEntity;
|
||||
import org.diylc.plugins.chatbot.service.ChatbotService;
|
||||
import org.diylc.plugins.cloud.service.NotLoggedInException;
|
||||
import org.diylc.swing.ISwingUI;
|
||||
import org.diylc.utils.FileUtils;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Random;
|
||||
|
||||
public class ChatbotPane extends JPanel {
|
||||
|
||||
@@ -43,7 +45,7 @@ public class ChatbotPane extends JPanel {
|
||||
private JButton clearButton;
|
||||
|
||||
private boolean loggedIn = false;
|
||||
private String projectFileName = null;
|
||||
private String projectFileName = "initial";
|
||||
|
||||
public ChatbotPane(ISwingUI swingUI, IPlugInPort plugInPort) {
|
||||
super();
|
||||
@@ -202,7 +204,7 @@ public class ChatbotPane extends JPanel {
|
||||
|
||||
public void refreshChat() {
|
||||
boolean currentLoggedIn = plugInPort.getCloudService().isLoggedIn();
|
||||
String currentProjectFileName = plugInPort.getCurrentFileName();
|
||||
String currentProjectFileName = FileUtils.extractFileName(plugInPort.getCurrentFileName());
|
||||
|
||||
if (this.loggedIn == currentLoggedIn && Objects.equals(currentProjectFileName, this.projectFileName))
|
||||
return;
|
||||
@@ -210,17 +212,26 @@ public class ChatbotPane extends JPanel {
|
||||
this.loggedIn = currentLoggedIn;
|
||||
this.projectFileName = currentProjectFileName;
|
||||
|
||||
resetChat();
|
||||
try {
|
||||
SubscriptionEntity subscriptionInfo = plugInPort.getChatbotService().getSubscriptionInfo();
|
||||
appendSection(ChatbotService.SYSTEM, "Subscribed to " + subscriptionInfo.getTier() +
|
||||
" tier with " + subscriptionInfo.getRemainingCredits() + " remaining credits.");
|
||||
} catch (NotLoggedInException e) {
|
||||
LOG.error("Error getting subscription info", e);
|
||||
appendSection(ChatbotService.SYSTEM, "Error getting subscription info");
|
||||
clearChat();
|
||||
if (!currentLoggedIn) {
|
||||
appendSection(ChatbotService.SYSTEM, "Please log into your cloud account to use the AI assistant");
|
||||
getAskButton().setEnabled(false);
|
||||
getClearButton().setEnabled(false);
|
||||
} else {
|
||||
try {
|
||||
SubscriptionEntity subscriptionInfo = plugInPort.getChatbotService().getSubscriptionInfo();
|
||||
appendSection(ChatbotService.SYSTEM,
|
||||
"Your current subscription tier is " + subscriptionInfo.getTier() +
|
||||
" expiring on " + subscriptionInfo.getEndDate() + " with " +
|
||||
subscriptionInfo.getRemainingCredits() + " credits remaining.");
|
||||
} catch (NotLoggedInException e) {
|
||||
LOG.error("Error getting subscription info", e);
|
||||
appendSection(ChatbotService.SYSTEM, "Error getting subscription info");
|
||||
}
|
||||
fetchChatHistory();
|
||||
getAskButton().setEnabled(true);
|
||||
getClearButton().setEnabled(true);
|
||||
}
|
||||
fetchChatHistory();
|
||||
getAskButton().setEnabled(true);
|
||||
}
|
||||
|
||||
private void fetchChatHistory() {
|
||||
@@ -250,13 +261,32 @@ public class ChatbotPane extends JPanel {
|
||||
return chatEditorPane;
|
||||
}
|
||||
|
||||
private void resetChat() {
|
||||
String sampleChat = chatbotService.getInitialChatContents();
|
||||
private void clearChat() {
|
||||
String sampleChat = getInitialChatContents();
|
||||
|
||||
chatEditorPane.setContentType("text/html");
|
||||
chatEditorPane.setText(HTML_STYLE + "<body>" + sampleChat + "</body>");
|
||||
}
|
||||
|
||||
private String getInitialChatContents() {
|
||||
Random r = new Random();
|
||||
|
||||
String chatHtml =
|
||||
"<div class='system'>" + ChatbotService.WELCOME_MESSAGE + "</div><br><br>\n" +
|
||||
"<div class='assistant'>I'm here to help you design, build, and troubleshoot your electronics projects using DIY Layout Creator (DIYLC). You can ask me questions about:</div><br>\n" +
|
||||
// "<ul>\n" +
|
||||
"\n" +
|
||||
"- <b>Electronics theory or concepts:</b> <i>e.g.,</i> '<span class='user'>" +ChatbotService.electronicsQuestions[r.nextInt(
|
||||
ChatbotService.electronicsQuestions.length)] + "</span>'<br>\n" +
|
||||
"- <b>DIYLC features and usage:</b> <i>e.g.,</i> '<span class='user'>" + ChatbotService.diylcQuestions[r.nextInt(ChatbotService.diylcQuestions.length)] + "</span>'<br>\n" +
|
||||
"- <b>Your current circuit project:</b> <i>e.g.,</i> '<span class='user'>" + ChatbotService.circuitQuestions[r.nextInt(ChatbotService.circuitQuestions.length)] + "</span>'<br>\n" +
|
||||
// "</ul><br>\n" +
|
||||
"<br>\n" +
|
||||
"<div class='assistant'>Feel free to ask your own question or use one of these examples to get started!</div><br>\n";
|
||||
|
||||
return chatHtml;
|
||||
}
|
||||
|
||||
public JButton getAskButton() {
|
||||
if (askButton == null) {
|
||||
askButton = new JButton("Ask");
|
||||
@@ -337,7 +367,7 @@ public class ChatbotPane extends JPanel {
|
||||
|
||||
@Override
|
||||
public void complete(Void result) {
|
||||
resetChat();
|
||||
clearChat();
|
||||
}
|
||||
}, false);
|
||||
});
|
||||
|
||||
@@ -43,35 +43,18 @@ public class ChatbotPlugin implements IPlugIn {
|
||||
public ChatbotPlugin(ISwingUI swingUI) {
|
||||
super();
|
||||
this.swingUI = swingUI;
|
||||
|
||||
// Delay a test of logged in state to give it time to complete
|
||||
Timer timer = new Timer(2000, e -> {
|
||||
if (!plugInPort.getCloudService().isLoggedIn()) {
|
||||
getChatbotPane().refreshChat();
|
||||
}
|
||||
});
|
||||
timer.setRepeats(false); // Make it one-shot
|
||||
timer.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<EventType> getSubscribedEventTypes() {
|
||||
return EnumSet.of(EventType.FILE_STATUS_CHANGED, EventType.PROJECT_LOADED,
|
||||
EventType.PROJECT_MODIFIED, EventType.SELECTION_CHANGED, EventType.CLOUD_LOGGED_IN,
|
||||
EventType.CLOUD_LOGGED_OUT);
|
||||
EventType.CLOUD_LOGGED_IN, EventType.CLOUD_LOGGED_OUT);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void processMessage(EventType eventType, Object... params) {
|
||||
switch (eventType) {
|
||||
case PROJECT_MODIFIED:
|
||||
// getChatbotPane().setComponents(((Project) params[1]).getComponents(),
|
||||
// plugInPort.getSelectedComponents());
|
||||
break;
|
||||
case SELECTION_CHANGED:
|
||||
// getChatbotPane().setSelection((Set<IDIYComponent<?>>) params[0]);
|
||||
break;
|
||||
case FILE_STATUS_CHANGED:
|
||||
case PROJECT_LOADED:
|
||||
case CLOUD_LOGGED_IN:
|
||||
|
||||
@@ -108,33 +108,6 @@ public class CloudPlugIn implements IPlugIn {
|
||||
public void connect(IPlugInPort plugInPort) {
|
||||
this.plugInPort = plugInPort;
|
||||
this.cloudService = plugInPort.getCloudService();
|
||||
|
||||
initialize();
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
this.swingUI.executeBackgroundTask(new ITask<Boolean>() {
|
||||
|
||||
@Override
|
||||
public Boolean doInBackground() throws Exception {
|
||||
return cloudService.tryLogInWithToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Exception e) {
|
||||
LOG.error("Error while trying to login using token");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void complete(Boolean result) {
|
||||
try {
|
||||
if (result)
|
||||
loggedIn();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error while trying to login with token", e);
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
public CloudBrowserFrame getCloudBrowser() {
|
||||
@@ -206,11 +179,20 @@ public class CloudPlugIn implements IPlugIn {
|
||||
|
||||
@Override
|
||||
public EnumSet<EventType> getSubscribedEventTypes() {
|
||||
return null;
|
||||
return EnumSet.of(EventType.CLOUD_LOGGED_IN, EventType.CLOUD_LOGGED_OUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processMessage(EventType eventType, Object... params) {}
|
||||
public void processMessage(EventType eventType, Object... params) {
|
||||
switch (eventType) {
|
||||
case CLOUD_LOGGED_IN:
|
||||
loggedIn();
|
||||
break;
|
||||
case CLOUD_LOGGED_OUT:
|
||||
loggedOut();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
class LibraryAction extends AbstractAction {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user