pax_global_header00006660000000000000000000000064131210040610014476gustar00rootroot0000000000000052 comment=9cda43b1378ae2dfb89a9b523634a89233cb4c7a ucrpf1host-0.0.20170617/000077500000000000000000000000001312100406100143415ustar00rootroot00000000000000ucrpf1host-0.0.20170617/README.md000066400000000000000000000014121312100406100156160ustar00rootroot00000000000000 # ucrpf1host This program is a host program for a 3D printer called Panowin F1. It can also be used on other 3D printers but beware that it might not work perfectly. Panowin F1 has some problems on its serial port circuit of its motherboard. The error rate is very high and makes the g-code broken often. Also their firmware is not the standard Marlin so it needs some special care when sending out the G-Codes. Like if the error eats the newline character, then we need to implement a timeout in host to resend the command when newline is missing. This host program should be used under Linux. On Windows it is probably to use pango software (the host and slicer) made by Panowin. It uses proprietary binary P-code which deals better with the high transmission error rate. ucrpf1host-0.0.20170617/build.xml000066400000000000000000000046211312100406100161650ustar00rootroot00000000000000 ucrpf1host-0.0.20170617/com/000077500000000000000000000000001312100406100151175ustar00rootroot00000000000000ucrpf1host-0.0.20170617/com/ucrobotics/000077500000000000000000000000001312100406100172735ustar00rootroot00000000000000ucrpf1host-0.0.20170617/com/ucrobotics/yliu/000077500000000000000000000000001312100406100202555ustar00rootroot00000000000000ucrpf1host-0.0.20170617/com/ucrobotics/yliu/ucrpf1host/000077500000000000000000000000001312100406100223535ustar00rootroot00000000000000ucrpf1host-0.0.20170617/com/ucrobotics/yliu/ucrpf1host/CommandData.java000066400000000000000000000125151312100406100253720ustar00rootroot00000000000000/* Copyright (C) 2017 Ying-Chun Liu (PaulLiu) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package com.ucrobotics.yliu.ucrpf1host; import java.util.logging.*; import java.util.*; import gnu.io.*; /** * Represent a G-Code command */ public class CommandData { private String command=null; private ArrayList answer=null; private int lineNumber = 0; private long timestamp=0; private double prevExtruderX=0.0; private double prevExtruderY=0.0; private double prevExtruderZ=0.0; private double prevExtruderE=0.0; private double prevExtruderF=0.0; /** * Constuct a CommandData with the G-Code command as a String. * * @gCodeCommand the G-Code string */ public CommandData(String gCodeCommand) { this.answer = new ArrayList(); this.command = gCodeCommand; } /** * Get the raw G-code of the CommandData * * @return the G-code string */ public String getCommand() { return command; } /** * Get the stripped G-code of the CommandData. * * It will remove the comments start with semi-comma (;) and also * removes all whitespaces in the g-code. * * @return the stripped G-code string */ public String getStripedCommand() { String ret = command; int commentIndex = ret.indexOf(';'); if (commentIndex >= 0) { ret = ret.substring(0, commentIndex); } ret = ret.replaceAll("\\s+", ""); return ret; } /** * Calculate the checksum of a command. * * You should calculate the checksum based on stripped command and with * the line number added. Not the raw one. * And before calculation you should convert it to bytes[] rather * than String. * * @s the g-code string in byte[] * @return the checksum. */ private int getChecksum(byte[] s) { int checksum=0; for (int i=0; i This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package com.ucrobotics.yliu.ucrpf1host; import java.util.logging.*; import java.util.*; import java.io.*; /** * A thread to send files to the device. */ public class FileCommandSender extends Thread { private PF1Device pf1Device = null; private boolean runningFlag = true; private File gcodeFile = null; private int numberOfLines = 0; private int currentLine = 0; private java.beans.PropertyChangeSupport mPcs = new java.beans.PropertyChangeSupport(this); private java.time.LocalDateTime startTime = null; public FileCommandSender(PF1Device pf1Device, File gcodeFile) { super(); this.pf1Device = pf1Device; this.gcodeFile = gcodeFile; this.runningFlag = true; this.numberOfLines = getLines(this.gcodeFile); this.startTime = java.time.LocalDateTime.now(); } public void pleaseStop() { this.runningFlag=false; } public int getNumberOfLines() { return numberOfLines; } public int getCurrentLine() { return currentLine; } public java.time.LocalDateTime getStartTime() { return startTime; } /** * Get the number of lines of a file */ private int getLines(File file) { int ret=0; FileReader in = null; BufferedReader br = null; try { in = new FileReader(file); } catch (FileNotFoundException e) { return ret; } br = new BufferedReader(in); String line = null; try { while ( (line = br.readLine()) != null) { ret = ret + 1; } } catch (IOException e) { return ret; } return ret; } public void run() { currentLine = 0; if (this.numberOfLines <= 0) { return; } FileReader in = null; BufferedReader br = null; try { in = new FileReader(gcodeFile); } catch (FileNotFoundException e) { return; } br = new BufferedReader(in); String line = null; try { while ( (line = br.readLine()) != null) { if (!runningFlag) { break; } int oldCurrentLine = currentLine; currentLine = currentLine + 1; mPcs.firePropertyChange("currentLine", new Integer(oldCurrentLine), new Integer(currentLine)); pf1Device.sendCommand(line); } } catch (IOException e) { return; } } /** * Add a PropertyChangeListener to the listener list. * * The listener is registered for all properties. * The same listener object may be added more than once, and will be * called as many times as it is added. If listener is null, no exception * is thrown and no action is taken. * * @listener The PropertyChangeListener to be added */ public void addPropertyChangeListener(java.beans.PropertyChangeListener listener) { mPcs.addPropertyChangeListener(listener); } /** * Add a PropertyChangeListener for a specific property. * * The listener will be invoked only when a call on firePropertyChange * names that specific property. The same listener object may be added * more than once. For each property, the listener will be invoked the * number of times it was added for that property. If propertyName or * listener is null, no exception is thrown and no action is taken. * * @propertyName The name of the property to listen on. * @listener The PropertyChangeListener to be added */ public void addPropertyChangeListener(String propertyName, java.beans.PropertyChangeListener listener) { mPcs.addPropertyChangeListener(propertyName, listener); } /** * Remove a PropertyChangeListener from the listener list. * * This removes a PropertyChangeListener that was registered for all * properties. If listener was added more than once to the same event * source, it will be notified one less time after being removed. If * listener is null, or was never added, no exception is thrown and no * action is taken. * * @listener The PropertyChangeListener to be removed */ public void removePropertyChangeListener(java.beans.PropertyChangeListener listener) { mPcs.removePropertyChangeListener(listener); } } ucrpf1host-0.0.20170617/com/ucrobotics/yliu/ucrpf1host/GlobalSettings.java000066400000000000000000000106641312100406100261460ustar00rootroot00000000000000/* Copyright (C) 2017 Ying-Chun Liu (PaulLiu) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package com.ucrobotics.yliu.ucrpf1host; public class GlobalSettings { private double extruderPreheatTemperature = 215.0; private double bedWidth = 120; private double bedHeight = 120; private double maxZ = 120; private java.beans.PropertyChangeSupport mPcs = new java.beans.PropertyChangeSupport(this); private static GlobalSettings instance = null; private GlobalSettings() { } public static GlobalSettings getInstance() { if (instance == null) { instance = new GlobalSettings(); } return instance; } public double getExtruderPreheatTemperature() { return extruderPreheatTemperature; } public void setExtruderPreheatTemperature(double extruderPreheatTemperature) { double oldTemperature = this.extruderPreheatTemperature; this.extruderPreheatTemperature = extruderPreheatTemperature; if (oldTemperature != extruderPreheatTemperature) { mPcs.firePropertyChange("extruderPreheatTemperature", new Double(oldTemperature), new Double(extruderPreheatTemperature)); } } public double getBedWidth() { return bedWidth; } public void setBedWidth(double bedWidth) { double oldBedWidth = this.bedWidth; this.bedWidth = bedWidth; if (oldBedWidth != bedWidth) { mPcs.firePropertyChange("bedWidth", new Double(oldBedWidth), new Double(bedWidth)); } } public double getBedHeight() { return bedHeight; } public void setBedHeight(double bedHeight) { double oldBedHeight = this.bedHeight; this.bedHeight = bedHeight; if (oldBedHeight != bedHeight) { mPcs.firePropertyChange("bedHeight", new Double(oldBedHeight), new Double(bedHeight)); } } public double getMaxZ() { return maxZ; } public void setMaxZ(double maxZ) { double oldMaxZ = this.maxZ; this.maxZ = maxZ; if (oldMaxZ != maxZ) { mPcs.firePropertyChange("maxZ", new Double(oldMaxZ), new Double(maxZ)); } } /** * Add a PropertyChangeListener to the listener list. * * The listener is registered for all properties. * The same listener object may be added more than once, and will be * called as many times as it is added. If listener is null, no exception * is thrown and no action is taken. * * @listener The PropertyChangeListener to be added */ public void addPropertyChangeListener(java.beans.PropertyChangeListener listener) { mPcs.addPropertyChangeListener(listener); } /** * Add a PropertyChangeListener for a specific property. * * The listener will be invoked only when a call on firePropertyChange * names that specific property. The same listener object may be added * more than once. For each property, the listener will be invoked the * number of times it was added for that property. If propertyName or * listener is null, no exception is thrown and no action is taken. * * @propertyName The name of the property to listen on. * @listener The PropertyChangeListener to be added */ public void addPropertyChangeListener(String propertyName, java.beans.PropertyChangeListener listener) { mPcs.addPropertyChangeListener(propertyName, listener); } /** * Remove a PropertyChangeListener from the listener list. * * This removes a PropertyChangeListener that was registered for all * properties. If listener was added more than once to the same event * source, it will be notified one less time after being removed. If * listener is null, or was never added, no exception is thrown and no * action is taken. * * @listener The PropertyChangeListener to be removed */ public void removePropertyChangeListener(java.beans.PropertyChangeListener listener) { mPcs.removePropertyChangeListener(listener); } } ucrpf1host-0.0.20170617/com/ucrobotics/yliu/ucrpf1host/LoadFilamentCommandSender.java000066400000000000000000000114241312100406100302170ustar00rootroot00000000000000/* Copyright (C) 2017 Ying-Chun Liu (PaulLiu) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package com.ucrobotics.yliu.ucrpf1host; import java.util.logging.*; import java.util.*; /** * A thread to load filament. */ public class LoadFilamentCommandSender extends Thread { private PF1Device pf1Device = null; private boolean runningFlag = true; private java.beans.PropertyChangeSupport mPcs = new java.beans.PropertyChangeSupport(this); public LoadFilamentCommandSender(PF1Device pf1Device) { super(); this.pf1Device = pf1Device; this.runningFlag = true; } public void pleaseStop() { this.runningFlag=false; } public void run() { pf1Device.sendCommand("G21"); /* homing */ mPcs.firePropertyChange("status", "", "Homing"); if (!runningFlag) { return; } pf1Device.sendCommand("G28 X0"); if (!runningFlag) { return; } pf1Device.sendCommand(String.format("G1 X%1$.2f F3600", GlobalSettings.getInstance().getBedWidth()/2.0)); if (!runningFlag) { return; } pf1Device.sendCommand("G28 Y0"); if (!runningFlag) { return; } pf1Device.sendCommand(String.format("G1 Y%1$.2f F3600", GlobalSettings.getInstance().getBedHeight()/2.0)); if (!runningFlag) { return; } pf1Device.sendCommand("G28 Z0"); if (!runningFlag) { return; } pf1Device.sendCommand(String.format("G1 Z%1$.2f F3000", GlobalSettings.getInstance().getMaxZ()/2.0)); /* heating */ mPcs.firePropertyChange("status", "Homing", "Heating"); if (!runningFlag) { return; } pf1Device.sendCommand(String.format("M104 S%1$d", Math.round(GlobalSettings.getInstance().getExtruderPreheatTemperature()))); if (!runningFlag) { return; } pf1Device.sendCommand(String.format("M109 S%1$d", Math.round(GlobalSettings.getInstance().getExtruderPreheatTemperature()))); while (runningFlag) { double t1 = pf1Device.getExtruderTemperature(); if (t1 + 0.5 >= GlobalSettings.getInstance().getExtruderPreheatTemperature()) { break; } try { Thread.sleep(3000); } catch (InterruptedException e1) { } } /* extruding */ mPcs.firePropertyChange("status", "Heating", "Loading"); while (runningFlag) { pf1Device.sendCommand("G92 E0"); pf1Device.sendCommand("G1 E5.0 F300"); pf1Device.sendCommand("G92 E0"); } mPcs.firePropertyChange("status", "Loading", ""); } /** * Add a PropertyChangeListener to the listener list. * * The listener is registered for all properties. * The same listener object may be added more than once, and will be * called as many times as it is added. If listener is null, no exception * is thrown and no action is taken. * * @listener The PropertyChangeListener to be added */ public void addPropertyChangeListener(java.beans.PropertyChangeListener listener) { mPcs.addPropertyChangeListener(listener); } /** * Add a PropertyChangeListener for a specific property. * * The listener will be invoked only when a call on firePropertyChange * names that specific property. The same listener object may be added * more than once. For each property, the listener will be invoked the * number of times it was added for that property. If propertyName or * listener is null, no exception is thrown and no action is taken. * * @propertyName The name of the property to listen on. * @listener The PropertyChangeListener to be added */ public void addPropertyChangeListener(String propertyName, java.beans.PropertyChangeListener listener) { mPcs.addPropertyChangeListener(propertyName, listener); } /** * Remove a PropertyChangeListener from the listener list. * * This removes a PropertyChangeListener that was registered for all * properties. If listener was added more than once to the same event * source, it will be notified one less time after being removed. If * listener is null, or was never added, no exception is thrown and no * action is taken. * * @listener The PropertyChangeListener to be removed */ public void removePropertyChangeListener(java.beans.PropertyChangeListener listener) { mPcs.removePropertyChangeListener(listener); } } ucrpf1host-0.0.20170617/com/ucrobotics/yliu/ucrpf1host/Main.java000066400000000000000000000071011312100406100241010ustar00rootroot00000000000000/* Copyright (C) 2017 Ying-Chun Liu (PaulLiu) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package com.ucrobotics.yliu.ucrpf1host; import java.util.*; import java.io.*; import java.awt.*; import java.awt.event.*; import java.net.*; import java.awt.datatransfer.*; import java.awt.image.*; import java.applet.*; import java.util.logging.*; import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; /** * Main class. The program entry */ public class Main { private Scanner stdin = new Scanner(System.in); private java.util.logging.Logger logger = null; public static String loggerName = "MainLogger"; private JApplet myapplet = null; private JFrame myframe = null; /** * Init class data here */ private void init() { myapplet = null; myframe = null; myapplet = new MyApplet(); myframe = new JFrame("ucrPF1host"); //myframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); myframe.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { Window w = e.getWindow(); myapplet.stop(); System.exit(0); } }); myframe.getContentPane().add(myapplet); myframe.setSize(640,480); myapplet.init(); myapplet.start(); myframe.setVisible(true); } /** * Handle the input here. * This method will call solve() method inside to solve the problem. * The return value indicates if there are more input data need to * be handled. If it doesn't return 0, means this function have to be * called again to solve next data. * @return 0: end. 1: need to call input() again for next data. */ private int input() { int ret=0; String com1; if (stdin.hasNextLine()) { com1 = stdin.nextLine(); } else { return ret; } solve(); ret=1; return ret; } /** * Solve the problems here. * It will call output to output the results. */ private void solve() { output(); } /** * Output the results */ private void output() { } /** * log information for debugging. */ public void logInfo(String a, Object... args) { if (logger != null) { logger.info(String.format(a,args)); } } public void begin() { this.logger = java.util.logging.Logger.getLogger(Main.loggerName); if (this.logger.getLevel() != java.util.logging.Level.INFO) { this.logger = null; } init(); } public void unittest() { this.logger = java.util.logging.Logger.getLogger(Main.loggerName); } public static void main (String args[]) { Main myMain = new Main(); if (args.length >= 1 && args[0].equals("unittest")) { myMain.unittest(); return; } java.util.logging.Logger.getLogger(Main.loggerName).setLevel(java.util.logging.Level.SEVERE); for (int i=0; args!=null && i This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package com.ucrobotics.yliu.ucrpf1host; import java.awt.*; import java.awt.event.*; import java.net.*; import java.awt.datatransfer.*; import java.awt.image.*; import java.applet.*; import java.util.logging.*; import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; import java.util.*; /** * This class is for the GUI. * Please implement all the UI logics here * * @author Paul Liu */ public class MyApplet extends JApplet { private JPanel cards = null; private java.util.logging.Logger logger = null; public static String loggerName = "MainLogger"; private PF1Device pf1Device = null; private LoadFilamentCommandSender loadFilamentThread = null; private UnloadFilamentCommandSender unloadFilamentThread = null; private FileCommandSender fileCommandSenderThread = null; private JTextField loadFilamentStatusJTextField = null; private JProgressBar printProgressJProgressBar = null; private JLabel printProgressCurrentLineJLabel = null; private JLabel printProgressTotalLineJLabel = null; private JLabel printProgressEstimateTimeJLabel = null; private JTextField loadFilamentTemperatureJTextField = null; private JTextField infoTemperatureJTextField = null; private PointPanel utilitiesExtruderPositionPanel = null; private JSlider utilitiesExtruderHeightPanel = null; private ResourceBundle resources = ResourceBundle.getBundle("ucrpf1host"); /** * init the UI layout and connect the ActionListeners */ public void init() { super.init(); Container cp = getContentPane(); this.logger = java.util.logging.Logger.getLogger(MyApplet.loggerName); this.logger.setLevel(java.util.logging.Level.INFO); cp.setLayout(new BorderLayout()); cards = new JPanel(new CardLayout()); cp.add(cards, BorderLayout.CENTER); JPanel mainPanel = createMainPanel(); cards.add(mainPanel, "MainPanel"); JPanel filamentPanel = createFilamentPanel(); cards.add(filamentPanel, "FilamentPanel"); JPanel connectPanel = createConnectPanel(); cards.add(connectPanel, "ConnectPanel"); JPanel printingInfoPanel = createPrintingInfoPanel(); cards.add(printingInfoPanel, "PrintingInfoPanel"); cards.add(createLoadFilamentPanel(), "LoadFilamentPanel"); cards.add(createSettingsPanel(), "SettingsPanel"); cards.add(createInfoPanel(), "InfoPanel"); cards.add(createUtilitiesPanel(), "UtilitiesPanel"); /* default card */ goToCard("ConnectPanel"); } /** * Create "PrintingInfoPanel". * This function should be only called once in * init(). * * @return a JPanel for "PrintingInfoPanel" */ private JPanel createPrintingInfoPanel() { JPanel printingInfoPanel = new JPanel(); printingInfoPanel.setLayout(new BorderLayout()); JButton backToMainButton = new JButton(resources.getString("Back_to_Main")); backToMainButton.addActionListener(new BackToMainButtonActionListener()); printingInfoPanel.add(backToMainButton, BorderLayout.SOUTH); JPanel progressPanel = new JPanel(); progressPanel.setLayout(new GridBagLayout()); JProgressBar progressBar = new JProgressBar(); this.printProgressJProgressBar = progressBar; GridBagConstraints progressBarGC = new GridBagConstraints(); progressBar.setIndeterminate(true); progressBar.setStringPainted(true); progressBarGC.gridx = 2; progressBarGC.gridy = 1; progressBarGC.gridwidth = 3; progressBarGC.gridheight = 1; progressBarGC.weightx = 1; progressBarGC.weighty = 0; progressBarGC.fill = GridBagConstraints.BOTH; progressBarGC.anchor = GridBagConstraints.WEST; progressPanel.add(progressBar, progressBarGC); JLabel progressLabel = new JLabel(resources.getString("Progress_COLON")); GridBagConstraints progressLabelGC = new GridBagConstraints(); progressLabelGC.gridx = 1; progressLabelGC.gridy = 1; progressLabelGC.gridwidth = 1; progressLabelGC.gridheight = 1; progressLabelGC.weightx = 1; progressLabelGC.weighty = 0; progressLabelGC.fill = GridBagConstraints.NONE; progressLabelGC.anchor = GridBagConstraints.EAST; progressPanel.add(progressLabel, progressLabelGC); JLabel linesLabel = new JLabel(resources.getString("Lines_COLON")); GridBagConstraints linesLabelGC = new GridBagConstraints(); linesLabelGC.gridx = 1; linesLabelGC.gridy = 2; linesLabelGC.gridwidth = 1; linesLabelGC.gridheight = 1; linesLabelGC.weightx = 1; linesLabelGC.weighty = 0; linesLabelGC.fill = GridBagConstraints.NONE; linesLabelGC.anchor = GridBagConstraints.EAST; progressPanel.add(linesLabel, linesLabelGC); JLabel currentLineLabel = new JLabel(); GridBagConstraints currentLineLabelGC = new GridBagConstraints(); currentLineLabelGC.gridx = 2; currentLineLabelGC.gridy = 2; currentLineLabelGC.gridwidth = 1; currentLineLabelGC.gridheight = 1; currentLineLabelGC.weightx = 1; currentLineLabelGC.weighty = 0; currentLineLabelGC.fill = GridBagConstraints.NONE; currentLineLabelGC.anchor = GridBagConstraints.CENTER; progressPanel.add(currentLineLabel, currentLineLabelGC); this.printProgressCurrentLineJLabel = currentLineLabel; JLabel slashLabel1 = new JLabel("/"); GridBagConstraints slashLabel1GC = new GridBagConstraints(); slashLabel1GC.gridx = 3; slashLabel1GC.gridy = 2; slashLabel1GC.gridwidth = 1; slashLabel1GC.gridheight = 1; slashLabel1GC.weightx = 1; slashLabel1GC.weighty = 0; slashLabel1GC.fill = GridBagConstraints.NONE; slashLabel1GC.anchor = GridBagConstraints.CENTER; progressPanel.add(slashLabel1, slashLabel1GC); JLabel totalLineLabel = new JLabel(); GridBagConstraints totalLineLabelGC = new GridBagConstraints(); totalLineLabelGC.gridx = 4; totalLineLabelGC.gridy = 2; totalLineLabelGC.gridwidth = 1; totalLineLabelGC.gridheight = 1; totalLineLabelGC.weightx = 1; totalLineLabelGC.weighty = 0; totalLineLabelGC.fill = GridBagConstraints.NONE; totalLineLabelGC.anchor = GridBagConstraints.CENTER; progressPanel.add(totalLineLabel, totalLineLabelGC); this.printProgressTotalLineJLabel = totalLineLabel; JLabel estimateTimeHeadLabel = new JLabel(resources.getString("Estimate_Time_COLON")); GridBagConstraints estimateTimeHeadLabelGC = new GridBagConstraints(); estimateTimeHeadLabelGC.gridx = 1; estimateTimeHeadLabelGC.gridy = 3; estimateTimeHeadLabelGC.gridwidth = 1; estimateTimeHeadLabelGC.gridheight = 1; estimateTimeHeadLabelGC.weightx = 1; estimateTimeHeadLabelGC.weighty = 0; estimateTimeHeadLabelGC.fill = GridBagConstraints.NONE; estimateTimeHeadLabelGC.anchor = GridBagConstraints.EAST; progressPanel.add(estimateTimeHeadLabel, estimateTimeHeadLabelGC); JLabel estimateTimeLabel = new JLabel(); GridBagConstraints estimateTimeLabelGC = new GridBagConstraints(); estimateTimeLabelGC.gridx = 2; estimateTimeLabelGC.gridy = 3; estimateTimeLabelGC.gridwidth = 3; estimateTimeLabelGC.gridheight = 1; estimateTimeLabelGC.weightx = 1; estimateTimeLabelGC.weighty = 0; estimateTimeLabelGC.fill = GridBagConstraints.NONE; estimateTimeLabelGC.anchor = GridBagConstraints.CENTER; progressPanel.add(estimateTimeLabel, estimateTimeLabelGC); this.printProgressEstimateTimeJLabel = estimateTimeLabel; printingInfoPanel.add(progressPanel, BorderLayout.CENTER); return printingInfoPanel; } /** * Create "ConnectPanel". * This function should be only called once in * init(). * * @return a JPanel for "ConnectPanel" */ private JPanel createConnectPanel() { JPanel connectPanel = new JPanel(); connectPanel.setLayout(new FlowLayout()); ArrayList devices = PF1Device.listDevices(); JComboBox deviceComboBox = new JComboBox(); for (String dev : devices) { deviceComboBox.addItem(dev); } JButton connectButton = new JButton(resources.getString("Connect")); connectButton.addActionListener(new ConnectButtonActionListener(deviceComboBox)); connectPanel.add(deviceComboBox); connectPanel.add(connectButton); return connectPanel; } /** * Create "FilamentPanel". * This function should be only called once in * init(). * * @return a JPanel for "FilamentPanel" */ private JPanel createFilamentPanel() { JPanel filamentPanel = new JPanel(); filamentPanel.setLayout(new BorderLayout()); JButton loadFilamentButton = new JButton(resources.getString("Load_Filament"), loadIcon("/images/loadfilament.png", 200)); loadFilamentButton.addActionListener(new LoadFilamentButtonActionListener()); JButton unloadFilamentButton = new JButton(resources.getString("Unload_Filament"), loadIcon("/images/unloadfilament.png", 200)); unloadFilamentButton.addActionListener(new UnloadFilamentButtonActionListener()); JPanel filamentPanelBox1 = new JPanel(); filamentPanelBox1.setLayout(new GridLayout(1,2)); JButton backToMainButton = new JButton(resources.getString("Back_to_Main")); backToMainButton.addActionListener(new BackToMainButtonActionListener()); filamentPanelBox1.add(loadFilamentButton); filamentPanelBox1.add(unloadFilamentButton); filamentPanel.add(filamentPanelBox1, BorderLayout.CENTER); filamentPanel.add(backToMainButton, BorderLayout.SOUTH); return filamentPanel; } /** * Create "LoadFilamentPanel". * This function should be only called once in * init(). * * @return a JPanel for "FilamentPanel" */ private JPanel createLoadFilamentPanel() { JPanel loadFilamentPanel = new JPanel(); loadFilamentPanel.setLayout(new BorderLayout()); JLabel label1 = new JLabel(resources.getString("Temperature_COLON")); JTextField textField1 = new JTextField(); JLabel label2 = new JLabel(resources.getString("Status_COLON")); JTextField textField2 = new JTextField(); this.loadFilamentTemperatureJTextField = textField1; this.loadFilamentStatusJTextField = textField2; JPanel loadFilamentPanelBox1 = new JPanel(); loadFilamentPanelBox1.setLayout(new GridLayout(2,2)); JButton stopButton = new JButton(resources.getString("Stop_Loading")); stopButton.addActionListener(new StopLoadFilamentButtonActionListener()); loadFilamentPanelBox1.add(label1); loadFilamentPanelBox1.add(textField1); loadFilamentPanelBox1.add(label2); loadFilamentPanelBox1.add(textField2); loadFilamentPanel.add(loadFilamentPanelBox1, BorderLayout.CENTER); loadFilamentPanel.add(stopButton, BorderLayout.SOUTH); return loadFilamentPanel; } /** * Create "SettingsPanel". * This function should be only called once in * init(). * * @return a JPanel for "SettingsPanel" */ private JPanel createSettingsPanel() { JPanel settingsPanel = new JPanel(); settingsPanel.setLayout(new BorderLayout()); JButton backToMainButton = new JButton(resources.getString("Back_to_Main")); backToMainButton.addActionListener(new BackToMainButtonActionListener()); settingsPanel.add(backToMainButton, BorderLayout.SOUTH); JPanel centerPanel = new JPanel(); centerPanel.setLayout(new GridBagLayout()); JLabel preheatTemperatureLabel = new JLabel(resources.getString("Preheat_Temperature")); GridBagConstraints preheatTemperatureLabelGC = new GridBagConstraints(); preheatTemperatureLabelGC.gridx = 1; preheatTemperatureLabelGC.gridy = 1; preheatTemperatureLabelGC.gridwidth = 1; preheatTemperatureLabelGC.gridheight = 1; preheatTemperatureLabelGC.weightx = 1; preheatTemperatureLabelGC.weighty = 0; preheatTemperatureLabelGC.fill = GridBagConstraints.NONE; preheatTemperatureLabelGC.anchor = GridBagConstraints.EAST; centerPanel.add(preheatTemperatureLabel, preheatTemperatureLabelGC); JSpinner preheatTemperature = new JSpinner (new SpinnerNumberModel(new Integer((int)Math.round(GlobalSettings.getInstance().getExtruderPreheatTemperature())), new Integer(100), new Integer(260), new Integer(1))); GridBagConstraints preheatTemperatureGC = new GridBagConstraints(); preheatTemperatureGC.gridx = 2; preheatTemperatureGC.gridy = 1; preheatTemperatureGC.gridwidth = 3; preheatTemperatureGC.gridheight = 1; preheatTemperatureGC.weightx = 1; preheatTemperatureGC.weighty = 0; preheatTemperatureGC.fill = GridBagConstraints.BOTH; preheatTemperatureGC.anchor = GridBagConstraints.WEST; centerPanel.add(preheatTemperature, preheatTemperatureGC); preheatTemperature.addChangeListener(new SettingsExtruderPreheatTemperatureChangeListener()); settingsPanel.add(centerPanel, BorderLayout.CENTER); return settingsPanel; } /** * Create "InfoPanel". * This function should be only called once in * init(). * * @return a JPanel for "InfoPanel" */ private JPanel createInfoPanel() { JPanel infoPanel = new JPanel(); infoPanel.setLayout(new BorderLayout()); JButton backToMainButton = new JButton(resources.getString("Back_to_Main")); backToMainButton.addActionListener(new BackToMainButtonActionListener()); infoPanel.add(backToMainButton, BorderLayout.SOUTH); JPanel centerPanel = new JPanel(); centerPanel.setLayout(new BoxLayout(centerPanel, BoxLayout.Y_AXIS)); JScrollPane centerPanelScroll = new JScrollPane(centerPanel); JPanel panel1 = new JPanel(); panel1.setLayout(new GridLayout(1,2)); JLabel extruderTemperatureLabel = new JLabel(resources.getString("Temperature_COLON")); panel1.add(extruderTemperatureLabel); JTextField temperatureField = new JTextField(); panel1.add(temperatureField); this.infoTemperatureJTextField = temperatureField; centerPanel.add(panel1); infoPanel.add(centerPanel, BorderLayout.CENTER); return infoPanel; } /** * Create "UtilitiesPanel". * This function should be only called once in * init(). * * @return a JPanel for "UtilitiesPanel" */ private JPanel createUtilitiesPanel() { JPanel utilitiesPanel = new JPanel(); utilitiesPanel.setLayout(new BorderLayout()); JButton backToMainButton = new JButton(resources.getString("Back_to_Main")); backToMainButton.addActionListener(new BackToMainButtonActionListener()); utilitiesPanel.add(backToMainButton, BorderLayout.SOUTH); JPanel centerPanel = new JPanel(); centerPanel.setLayout(new GridBagLayout()); JButton homeButton = new JButton(resources.getString("Home")); GridBagConstraints homeButtonGC = new GridBagConstraints(); homeButtonGC.gridx = 1; homeButtonGC.gridy = 1; homeButtonGC.gridwidth = 2; homeButtonGC.gridheight = 2; homeButtonGC.weightx = 0; homeButtonGC.weighty = 0; homeButtonGC.fill = GridBagConstraints.BOTH; homeButtonGC.anchor = GridBagConstraints.WEST; homeButton.addActionListener(new UtilitiesHomeButtonActionListener()); centerPanel.add(homeButton, homeButtonGC); JLabel extruderPositionLabel = new JLabel(resources.getString("Extruder_Position")); GridBagConstraints extruderPositionLabelGC = new GridBagConstraints(); extruderPositionLabelGC.gridx = 3; extruderPositionLabelGC.gridy = 3; extruderPositionLabelGC.gridwidth = 1; extruderPositionLabelGC.gridheight = 1; extruderPositionLabelGC.weightx = 1; extruderPositionLabelGC.weighty = 0; extruderPositionLabelGC.fill = GridBagConstraints.NONE; extruderPositionLabelGC.anchor = GridBagConstraints.EAST; centerPanel.add(extruderPositionLabel, extruderPositionLabelGC); PointPanel extruderPositionPanel = new PointPanel(); extruderPositionPanel.setPreferredSize(new Dimension(100,100)); GridBagConstraints extruderPositionPanelGC = new GridBagConstraints(); extruderPositionPanelGC.gridx = 4; extruderPositionPanelGC.gridy = 1; extruderPositionPanelGC.gridwidth = 3; extruderPositionPanelGC.gridheight = 3; extruderPositionPanelGC.weightx = 0; extruderPositionPanelGC.weighty = 0; extruderPositionPanelGC.fill = GridBagConstraints.NONE; extruderPositionPanelGC.anchor = GridBagConstraints.WEST; extruderPositionPanel.addMouseListener(new ExtruderPositionPanelMouseListener()); centerPanel.add(extruderPositionPanel, extruderPositionPanelGC); this.utilitiesExtruderPositionPanel = extruderPositionPanel; JLabel extruderHeightLabel = new JLabel(resources.getString("Extruder_Height")); GridBagConstraints extruderHeightLabelGC = new GridBagConstraints(); extruderHeightLabelGC.gridx = 9; extruderHeightLabelGC.gridy = 3; extruderHeightLabelGC.gridwidth = 1; extruderHeightLabelGC.gridheight = 1; extruderHeightLabelGC.weightx = 1; extruderHeightLabelGC.weighty = 0; extruderHeightLabelGC.fill = GridBagConstraints.NONE; extruderHeightLabelGC.anchor = GridBagConstraints.EAST; centerPanel.add(extruderHeightLabel, extruderHeightLabelGC); JSlider extruderHeightPanel = new JSlider(JSlider.VERTICAL,0,100000,0); GridBagConstraints extruderHeightPanelGC = new GridBagConstraints(); extruderHeightPanelGC.gridx = 10; extruderHeightPanelGC.gridy = 1; extruderHeightPanelGC.gridwidth = 2; extruderHeightPanelGC.gridheight = 3; extruderHeightPanelGC.weightx = 0; extruderHeightPanelGC.weighty = 0; extruderHeightPanelGC.fill = GridBagConstraints.NONE; extruderHeightPanelGC.anchor = GridBagConstraints.WEST; extruderHeightPanel.addMouseListener(new ExtruderPositionPanelMouseListener()); extruderHeightPanel.addChangeListener(new ExtruderHeightPanelChangeListener()); centerPanel.add(extruderHeightPanel, extruderHeightPanelGC); this.utilitiesExtruderHeightPanel = extruderHeightPanel; JLabel gCodeLabel = new JLabel(resources.getString("GCode")); GridBagConstraints gCodeLabelGC = new GridBagConstraints(); gCodeLabelGC.gridx = 1; gCodeLabelGC.gridy = 4; gCodeLabelGC.gridwidth = 1; gCodeLabelGC.gridheight = 1; gCodeLabelGC.weightx = 1; gCodeLabelGC.weighty = 0; gCodeLabelGC.fill = GridBagConstraints.NONE; gCodeLabelGC.anchor = GridBagConstraints.EAST; centerPanel.add(gCodeLabel, gCodeLabelGC); JTextField gCodeTextField = new JTextField(); GridBagConstraints gCodeTextFieldGC = new GridBagConstraints(); gCodeTextFieldGC.gridx = 2; gCodeTextFieldGC.gridy = 4; gCodeTextFieldGC.gridwidth = 3; gCodeTextFieldGC.gridheight = 1; gCodeTextFieldGC.weightx = 1; gCodeTextFieldGC.weighty = 0; gCodeTextFieldGC.fill = GridBagConstraints.BOTH; gCodeTextFieldGC.anchor = GridBagConstraints.EAST; centerPanel.add(gCodeTextField, gCodeTextFieldGC); JButton gCodeTextButton = new JButton(resources.getString("Send")); GridBagConstraints gCodeTextButtonGC = new GridBagConstraints(); gCodeTextButtonGC.gridx = 5; gCodeTextButtonGC.gridy = 4; gCodeTextButtonGC.gridwidth = 1; gCodeTextButtonGC.gridheight = 1; gCodeTextButtonGC.weightx = 1; gCodeTextButtonGC.weighty = 0; gCodeTextButtonGC.fill = GridBagConstraints.BOTH; gCodeTextButtonGC.anchor = GridBagConstraints.EAST; centerPanel.add(gCodeTextButton, gCodeTextButtonGC); gCodeTextButton.addActionListener(new UtilitiesGCodeSendButtonActionListener(gCodeTextField)); utilitiesPanel.add(centerPanel, BorderLayout.CENTER); return utilitiesPanel; } /** * Create "MainPanel". * This function should be only called once in * init(). * * @return a JPanel for "MainPanel" */ private JPanel createMainPanel() { JPanel mainPanel = new JPanel(); mainPanel.setLayout(new GridLayout(2,3)); JButton printButton = new JButton(resources.getString("Print"), loadIcon("/images/print.png", 70)); printButton.addActionListener(new PrintButtonActionListener()); JButton filamentButton = new JButton(resources.getString("Filament"), loadIcon("/images/filament.png", 70)); filamentButton.addActionListener(new FilamentButtonActionListener()); JToggleButton preheatButton = new JToggleButton(resources.getString("Preheat"), loadIcon("/images/preheat.png", 70)); preheatButton.addItemListener(new PreheatButtonActionListener()); JButton utilitiesButton = new JButton(resources.getString("Utilities"), loadIcon("/images/utilities.png", 70)); utilitiesButton.addActionListener(new UtilitiesButtonActionListener()); JButton settingsButton = new JButton(resources.getString("Settings"), loadIcon("/images/settings.png", 70)); settingsButton.addActionListener(new SettingsButtonActionListener()); JButton infoButton = new JButton(resources.getString("Info"), loadIcon("/images/info.png", 70)); infoButton.addActionListener(new InfoButtonActionListener()); mainPanel.add(printButton); mainPanel.add(filamentButton); mainPanel.add(preheatButton); mainPanel.add(utilitiesButton); mainPanel.add(settingsButton); mainPanel.add(infoButton); return mainPanel; } /** * Go to the card registered by name * * @name the name of the card */ public void goToCard(String name) { ((CardLayout)cards.getLayout()).show(cards, name); } /** * Load an icon from jar and resize the maximum side to iconSize * * @fileName the filename of the icon in jar * @iconSize resize the icon to iconSize * @return an ImageIcon */ private ImageIcon loadIcon(String fileName, int iconSize){ ImageIcon ret = null; ImageIcon orig = null; orig = new javax.swing.ImageIcon(getClass().getResource(fileName)); if (orig != null) { Image im = orig.getImage(); Image imScaled = null; if (im.getWidth(null) > im.getHeight(null)) { imScaled = im.getScaledInstance(iconSize,-1,Image.SCALE_SMOOTH); } else { imScaled = im.getScaledInstance(-1,iconSize,Image.SCALE_SMOOTH); } ret = new javax.swing.ImageIcon(imScaled); } return ret; } /** * Stop the applet. Close the serial connections. * */ public void stop() { if (loadFilamentThread != null) { loadFilamentThread.pleaseStop(); try { loadFilamentThread.join(2000); } catch (InterruptedException e1) { e1.printStackTrace(); } loadFilamentThread = null; } if (unloadFilamentThread != null) { unloadFilamentThread.pleaseStop(); try { unloadFilamentThread.join(2000); } catch (InterruptedException e1) { e1.printStackTrace(); } unloadFilamentThread = null; } if (fileCommandSenderThread != null) { fileCommandSenderThread.pleaseStop(); try { fileCommandSenderThread.join(2000); } catch (InterruptedException e1) { e1.printStackTrace(); } fileCommandSenderThread = null; printProgressJProgressBar.setIndeterminate(true); } if (pf1Device != null) { pf1Device.close(5000); pf1Device = null; } super.stop(); } /** * The ActionListener for PrintButton in MainPanel */ class PrintButtonActionListener implements ActionListener { public void actionPerformed(ActionEvent e) { logger.info("Print"); JFileChooser chooser = new JFileChooser(); javax.swing.filechooser.FileNameExtensionFilter filter = new javax.swing.filechooser.FileNameExtensionFilter("GCode files", "gcode"); chooser.setFileFilter(filter); int returnVal = chooser.showOpenDialog(MyApplet.this); if (returnVal == JFileChooser.APPROVE_OPTION) { logger.info("You chose to open this file: " + chooser.getSelectedFile().getName()); fileCommandSenderThread = new FileCommandSender(pf1Device, chooser.getSelectedFile()); fileCommandSenderThread.addPropertyChangeListener("currentLine", new FileCommandSenderProgressChangeListener(printProgressJProgressBar, printProgressCurrentLineJLabel, printProgressTotalLineJLabel, printProgressEstimateTimeJLabel)); fileCommandSenderThread.start(); goToCard("PrintingInfoPanel"); } } } /** * The ActionListener for FilamentButton in MainPanel */ class FilamentButtonActionListener implements ActionListener { public void actionPerformed(ActionEvent e) { logger.info("Filament"); goToCard("FilamentPanel"); } } /** * The ItemListener for PreheatButton in MainPanel */ class PreheatButtonActionListener implements ActionListener, ItemListener { public void actionPerformed(ActionEvent e) { logger.info("Preheat"); } public void itemStateChanged(java.awt.event.ItemEvent e1) { if (e1.getStateChange() == ItemEvent.SELECTED) { logger.info("Preheat selected"); if (pf1Device != null) { pf1Device.sendCommand(String.format("M104 S%1$d", Math.round(GlobalSettings.getInstance().getExtruderPreheatTemperature()))); } } else if (e1.getStateChange() == ItemEvent.DESELECTED) { logger.info("Preheat deselected"); if (pf1Device != null) { pf1Device.sendCommand("M104 S0"); } } } } /** * The ActionListener for UtilitiesButton in MainPanel */ class UtilitiesButtonActionListener implements ActionListener { public void actionPerformed(ActionEvent e) { logger.info("Utilities"); goToCard("UtilitiesPanel"); } } /** * The ActionListener for SettingsButton in MainPanel */ class SettingsButtonActionListener implements ActionListener { public void actionPerformed(ActionEvent e) { logger.info("Settings"); goToCard("SettingsPanel"); } } /** * The ActionListener for InfoButton in MainPanel */ class InfoButtonActionListener implements ActionListener { public void actionPerformed(ActionEvent e) { logger.info("Info"); goToCard("InfoPanel"); } } /** * The ActionListener for LoadFilamentButton in FilamentPanel */ class LoadFilamentButtonActionListener implements ActionListener { public void actionPerformed(ActionEvent e) { logger.info("Load Filamente"); loadFilamentThread = new LoadFilamentCommandSender(pf1Device); loadFilamentThread.addPropertyChangeListener("status", new MyAppletPropertyChangeListener(loadFilamentStatusJTextField)); loadFilamentThread.start(); goToCard("LoadFilamentPanel"); } } /** * The ActionListener for UnloadFilamentButton in FilamentPanel */ class UnloadFilamentButtonActionListener implements ActionListener { public void actionPerformed(ActionEvent e) { logger.info("Unload Filamente"); unloadFilamentThread = new UnloadFilamentCommandSender(pf1Device); unloadFilamentThread.addPropertyChangeListener("status", new MyAppletPropertyChangeListener(loadFilamentStatusJTextField)); unloadFilamentThread.start(); goToCard("LoadFilamentPanel"); } } /** * The ActionListener for StopLoadFilamentButton in LoadFilamentPanel */ class StopLoadFilamentButtonActionListener implements ActionListener { public void actionPerformed(ActionEvent e) { logger.info("Stop Load Filamente"); if (loadFilamentThread != null) { loadFilamentThread.pleaseStop(); try { loadFilamentThread.join(); } catch (InterruptedException e1) { e1.printStackTrace(); } loadFilamentThread = null; } if (unloadFilamentThread != null) { unloadFilamentThread.pleaseStop(); try { unloadFilamentThread.join(); } catch (InterruptedException e1) { e1.printStackTrace(); } unloadFilamentThread = null; } if (fileCommandSenderThread != null) { fileCommandSenderThread.pleaseStop(); try { fileCommandSenderThread.join(); } catch (InterruptedException e1) { e1.printStackTrace(); } fileCommandSenderThread = null; printProgressJProgressBar.setIndeterminate(true); } goToCard("FilamentPanel"); } } /** * The ActionListener for BackToMainButton. Can be used in several cards. */ class BackToMainButtonActionListener implements ActionListener { public void actionPerformed(ActionEvent e) { logger.info("BackToMainButton"); if (loadFilamentThread != null) { loadFilamentThread.pleaseStop(); try { loadFilamentThread.join(); } catch (InterruptedException e1) { e1.printStackTrace(); } loadFilamentThread = null; } if (unloadFilamentThread != null) { unloadFilamentThread.pleaseStop(); try { unloadFilamentThread.join(); } catch (InterruptedException e1) { e1.printStackTrace(); } unloadFilamentThread = null; } if (fileCommandSenderThread != null) { fileCommandSenderThread.pleaseStop(); try { fileCommandSenderThread.join(); } catch (InterruptedException e1) { e1.printStackTrace(); } fileCommandSenderThread = null; printProgressJProgressBar.setIndeterminate(true); } goToCard("MainPanel"); } } /** * The ActionListener for ConnectButton in ConnectPanel */ class ConnectButtonActionListener implements ActionListener { private JComboBox deviceComboBox = null; public ConnectButtonActionListener(JComboBox deviceComboBox) { this.deviceComboBox = deviceComboBox; } public void actionPerformed(ActionEvent e) { logger.info("Connect"); try { pf1Device = new PF1Device((String)deviceComboBox.getSelectedItem()); } catch (java.io.FileNotFoundException e1) { logger.info("Get FileNotFoundException when creating PF1Device"); pf1Device = null; } catch (gnu.io.PortInUseException e1) { logger.info("Get PortInUseException when creating PF1Device"); pf1Device = null; } catch (gnu.io.UnsupportedCommOperationException e1) { logger.info("Get UnsupportedCommOperationException when creating PF1Device"); pf1Device = null; } if (pf1Device != null) { pf1Device.addPropertyChangeListener("extruderTemperature", new MyAppletPropertyChangeListener(loadFilamentTemperatureJTextField)); pf1Device.addPropertyChangeListener("extruderTemperature", new MyAppletPropertyChangeListener(infoTemperatureJTextField)); pf1Device.addPropertyChangeListener(new ExtruderPositionPanelUpdater(utilitiesExtruderPositionPanel, utilitiesExtruderHeightPanel)); goToCard("MainPanel"); } } } /** * The PropertyChangeListener for JLabel, JTextField */ class MyAppletPropertyChangeListener implements java.beans.PropertyChangeListener { private javax.swing.text.JTextComponent jTextComponent = null; private JLabel jLabel = null; public MyAppletPropertyChangeListener(javax.swing.text.JTextComponent jTextComponent) { this.jTextComponent = jTextComponent; } public MyAppletPropertyChangeListener(JLabel jLabel) { this.jLabel = jLabel; } public void propertyChange(java.beans.PropertyChangeEvent evt) { if (this.jTextComponent != null) { this.jTextComponent.setText(evt.getNewValue().toString()); } if (this.jLabel != null) { this.jLabel.setText(evt.getNewValue().toString()); } } } /** * This class listens to the progess from FileCommandSender class and * updates the JProgressBar, time estimation and the related stuff. */ class FileCommandSenderProgressChangeListener implements java.beans.PropertyChangeListener { JProgressBar jProgressBar = null; JLabel jCurrentLine = null; JLabel jTotalLine = null; JLabel jEstimateTime = null; public FileCommandSenderProgressChangeListener(JProgressBar jProgressBar, JLabel jCurrentLine, JLabel jTotalLine, JLabel jEstimateTime) { this.jProgressBar = jProgressBar; this.jCurrentLine = jCurrentLine; this.jTotalLine = jTotalLine; this.jEstimateTime = jEstimateTime; } public void propertyChange(java.beans.PropertyChangeEvent evt) { Object sourceO = evt.getSource(); FileCommandSender fcs = null; if (sourceO instanceof FileCommandSender) { fcs = (FileCommandSender)sourceO; } if (fcs == null) { if (this.jProgressBar != null) { this.jProgressBar.setIndeterminate(true); } return; } if (this.jProgressBar != null) { this.jProgressBar.setIndeterminate(false); this.jProgressBar.setMinimum(0); this.jProgressBar.setMaximum(fcs.getNumberOfLines()); this.jProgressBar.setValue(fcs.getCurrentLine()); } if (this.jCurrentLine != null) { this.jCurrentLine.setText(String.format("%1$d", fcs.getCurrentLine())); } if (this.jTotalLine != null) { this.jTotalLine.setText(String.format("%1$d", fcs.getNumberOfLines())); } java.time.LocalDateTime startTime = fcs.getStartTime(); if (startTime != null && fcs.getNumberOfLines() > 0 && fcs.getCurrentLine() > 0) { java.time.LocalDateTime currentTime = java.time.LocalDateTime.now(); long diffInSeconds = java.time.Duration.between(startTime, currentTime).getSeconds(); logger.finest(String.format("diffInSeconds: %1$d", diffInSeconds)); long estimatedSeconds = diffInSeconds * fcs.getNumberOfLines() / fcs.getCurrentLine(); logger.finest(String.format("estimatedSeconds: %1$d", estimatedSeconds)); java.time.LocalDateTime estimatedTime = startTime.plusSeconds(estimatedSeconds); logger.finest(String.format("estimatedTime: %1$s", estimatedTime.toString())); java.time.Duration estimatedDuration = java.time.Duration.between(startTime, estimatedTime); if (this.jEstimateTime != null) { String t1 = estimatedTime.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); this.jEstimateTime.setText(t1); } } } } class SettingsExtruderPreheatTemperatureChangeListener implements javax.swing.event.ChangeListener { public void stateChanged(ChangeEvent e) { Object sourceO = e.getSource(); if (sourceO instanceof JSpinner) { JSpinner jsp1 = (JSpinner)sourceO; int temp1 = Integer.parseInt( jsp1.getValue().toString() ); GlobalSettings.getInstance().setExtruderPreheatTemperature((double)temp1); } } } class ExtruderPositionPanelMouseListener implements MouseListener { public void mouseClicked(MouseEvent e) { int mx = e.getX(); int my = e.getY(); Object jPanelO = e.getSource(); PointPanel jPanel = null; if (jPanelO instanceof PointPanel) { jPanel = (PointPanel)jPanelO; } if (jPanel == null) { return; } if (e.getButton() == MouseEvent.BUTTON1) { double x = ((double)mx)/((double)jPanel.getWidth()); double y = ((double)my)/((double)jPanel.getHeight()); if (x<=0.0) { x=0.0; } if (x>=1.0) { x=1.0; } if (y<=0.0) { y=0.0; } if (y>=1.0) { y=1.0; } y=1.0-y; if (pf1Device != null) { pf1Device.sendCommand(String.format("G1 X%1$.2f Y%2$.2f F3600", x*GlobalSettings.getInstance().getBedWidth(), y*GlobalSettings.getInstance().getBedHeight())); } } } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public void mousePressed(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } } class ExtruderPositionPanelUpdater implements java.beans.PropertyChangeListener { PointPanel jPanel = null; JSlider jSlider = null; public ExtruderPositionPanelUpdater(PointPanel jPanel, JSlider jSlider) { this.jPanel = jPanel; this.jSlider= jSlider; } public void propertyChange(java.beans.PropertyChangeEvent evt) { if (evt.getPropertyName().compareTo("extruderX")==0) { double x = ((Double)(evt.getNewValue())).doubleValue(); double x1 = x / GlobalSettings.getInstance().getBedWidth(); if (x1 >= 1.0) { x1 = 1.0; } if (x1 <= 0.0) { x1 = 0.0; } this.jPanel.setPointX(x1); this.jPanel.repaint(); } else if (evt.getPropertyName().compareTo("extruderY")==0) { double y = ((Double)(evt.getNewValue())).doubleValue(); double y1 = y / GlobalSettings.getInstance().getBedHeight(); if (y1 >= 1.0) { y1 = 1.0; } if (y1 <= 0.0) { y1 = 0.0; } y1 = 1.0-y1; this.jPanel.setPointY(y1); this.jPanel.repaint(); } else if (evt.getPropertyName().compareTo("extruderZ")==0) { double z = ((Double)(evt.getNewValue())).doubleValue(); double z1 = z / GlobalSettings.getInstance().getMaxZ(); if (z1 < 0.0) { z1 = 0.0; } if (z1 >= 1.0) { z1 = 1.0; } int z2 = (int) Math.round(z1*((double)jSlider.getMaximum())); if (z2 != jSlider.getValue()) { ChangeListener[] listeners = jSlider.getChangeListeners(); for (int i1=0; i1= 1.0) { h1 = 1.0; } if (pf1Device != null) { if (valueChanged) { pf1Device.sendCommand(String.format("G1 Z%1$.5f F3000", h1*GlobalSettings.getInstance().getMaxZ())); } } valueChanged=false; } else { valueChanged=true; } } } } class UtilitiesGCodeSendButtonActionListener implements ActionListener { private JTextField jTextField=null; public UtilitiesGCodeSendButtonActionListener(JTextField jTextField) { this.jTextField = jTextField; } public void actionPerformed(ActionEvent e) { logger.info(String.format("Utilities -> Send Button pressed. Cmd=%1$s",jTextField.getText())); if (pf1Device != null) { pf1Device.sendCommand(jTextField.getText()); } jTextField.setText(""); } } class UtilitiesHomeButtonActionListener implements ActionListener { public void actionPerformed(ActionEvent e) { logger.info(String.format("Utilities -> Home Button pressed.")); if (pf1Device != null) { pf1Device.sendCommand("G28"); } } } } ucrpf1host-0.0.20170617/com/ucrobotics/yliu/ucrpf1host/PF1Device.java000066400000000000000000000221321312100406100247240ustar00rootroot00000000000000/* Copyright (C) 2017 Ying-Chun Liu (PaulLiu) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package com.ucrobotics.yliu.ucrpf1host; import java.util.logging.*; import java.util.*; import gnu.io.*; /** * Panowin F1 Device class */ public class PF1Device { private java.util.logging.Logger logger = null; public static String loggerName = "PF1DeviceLogger"; private SerialPort serialPort = null; private PF1DeviceServer pf1DeviceServer = null; private Thread pf1DeviceServerThread = null; private double extruderX = 0.0; private double extruderY = 0.0; private double extruderZ = 0.0; private double extruderE = 0.0; private double extruderF = 0.0; private double extruderTemperature = 0.0; private double extruderTargetTemperature = 0.0; private java.beans.PropertyChangeSupport mPcs = new java.beans.PropertyChangeSupport(this); /** * Constructor for PF1Device * * @devName the device name of the printer */ public PF1Device(String devName) throws java.io.FileNotFoundException, PortInUseException, UnsupportedCommOperationException { this.logger = java.util.logging.Logger.getLogger(PF1Device.loggerName); this.logger.setLevel(java.util.logging.Level.INFO); CommPortIdentifier portID = getCommPortIdentifierByName(devName); if (portID == null) { throw new java.io.FileNotFoundException(devName); } CommPort commPort = null; commPort = portID.open("ucrpf1host", 10); if (commPort == null) { throw new java.io.FileNotFoundException(devName); } serialPort = (SerialPort)commPort; serialPort.setSerialPortParams(115200, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); pf1DeviceServer = new PF1DeviceServer(this); pf1DeviceServerThread = new Thread(pf1DeviceServer); pf1DeviceServerThread.start(); } /** * get the Serial port of the PF1Device */ public SerialPort getSerialPort() { return serialPort; } /** * get the CommPortIdentifier by device name * * @devName the device name * @return null if not found */ private CommPortIdentifier getCommPortIdentifierByName(String devName) { Enumeration ports = CommPortIdentifier.getPortIdentifiers(); while (ports.hasMoreElements()) { CommPortIdentifier port = (CommPortIdentifier) ports.nextElement(); if (port.getPortType() != CommPortIdentifier.PORT_SERIAL) { continue; } if (port.getName().compareTo(devName) == 0) { return port; } } return null; } /** * List all serial devices * * @return the List of all devices */ public static ArrayList listDevices() { ArrayList ret = new ArrayList(); Enumeration ports = CommPortIdentifier.getPortIdentifiers(); while (ports.hasMoreElements()) { CommPortIdentifier port = (CommPortIdentifier) ports.nextElement(); java.util.logging.Logger.getLogger(PF1Device.loggerName).info(String.format("Found device name: %1$s", port.getName())); if (port.getPortType() != CommPortIdentifier.PORT_SERIAL) { java.util.logging.Logger.getLogger(PF1Device.loggerName).info(String.format("Device: %1$s is not a serial port", port.getName())); continue; } java.util.logging.Logger.getLogger(PF1Device.loggerName).info(String.format("Name: %1$s", port.getName())); ret.add(port.getName()); } return ret; } public double getExtruderX() { return extruderX; } public void setExtruderX(double extruderX) { double oldExtruderX = this.extruderX; this.extruderX = extruderX; java.util.logging.Logger.getLogger(PF1Device.loggerName).finer(String.format("ExtruderX: %1$f", extruderX)); if (oldExtruderX != extruderX) { mPcs.firePropertyChange("extruderX", new Double(oldExtruderX), new Double(extruderX)); } } public double getExtruderY() { return extruderY; } public void setExtruderY(double extruderY) { double oldExtruderY = this.extruderY; this.extruderY = extruderY; java.util.logging.Logger.getLogger(PF1Device.loggerName).finer(String.format("ExtruderY: %1$f", extruderY)); if (oldExtruderY != extruderY) { mPcs.firePropertyChange("extruderY", new Double(oldExtruderY), new Double(extruderY)); } } public double getExtruderZ() { return extruderZ; } public void setExtruderZ(double extruderZ) { double oldExtruderZ = this.extruderZ; this.extruderZ = extruderZ; if (oldExtruderZ != extruderZ) { mPcs.firePropertyChange("extruderZ", new Double(oldExtruderZ), new Double(extruderZ)); } } public double getExtruderE() { return extruderE; } public void setExtruderE(double extruderE) { double oldExtruderE = this.extruderE; this.extruderE = extruderE; if (oldExtruderE != extruderE) { mPcs.firePropertyChange("extruderE", new Double(oldExtruderE), new Double(extruderE)); } } public double getExtruderF() { return extruderF; } public void setExtruderF(double extruderF) { double oldExtruderF = this.extruderF; this.extruderF = extruderF; if (oldExtruderF != extruderF) { mPcs.firePropertyChange("extruderF", new Double(oldExtruderF), new Double(extruderF)); } } public void setExtruderTemperature(double extruderTemperature) { double oldTemperature = this.extruderTemperature; this.extruderTemperature = extruderTemperature; if (oldTemperature != extruderTemperature) { mPcs.firePropertyChange("extruderTemperature", new Double(oldTemperature), new Double(extruderTemperature)); } } public double getExtruderTargetTemperature() { return extruderTargetTemperature; } public void setExtruderTargetTemperature(double extruderTargetTemperature) { double oldTargetTemperature = this.extruderTargetTemperature; this.extruderTargetTemperature = extruderTargetTemperature; if (oldTargetTemperature != extruderTargetTemperature) { mPcs.firePropertyChange("extruderTargetTemperature", new Double(oldTargetTemperature), new Double(extruderTargetTemperature)); } } public double getExtruderTemperature() { return extruderTemperature; } /** * Add a PropertyChangeListener to the listener list. * * The listener is registered for all properties. * The same listener object may be added more than once, and will be * called as many times as it is added. If listener is null, no exception * is thrown and no action is taken. * * @listener The PropertyChangeListener to be added */ public void addPropertyChangeListener(java.beans.PropertyChangeListener listener) { mPcs.addPropertyChangeListener(listener); } /** * Add a PropertyChangeListener for a specific property. * * The listener will be invoked only when a call on firePropertyChange * names that specific property. The same listener object may be added * more than once. For each property, the listener will be invoked the * number of times it was added for that property. If propertyName or * listener is null, no exception is thrown and no action is taken. * * @propertyName The name of the property to listen on. * @listener The PropertyChangeListener to be added */ public void addPropertyChangeListener(String propertyName, java.beans.PropertyChangeListener listener) { mPcs.addPropertyChangeListener(propertyName, listener); } /** * Remove a PropertyChangeListener from the listener list. * * This removes a PropertyChangeListener that was registered for all * properties. If listener was added more than once to the same event * source, it will be notified one less time after being removed. If * listener is null, or was never added, no exception is thrown and no * action is taken. * * @listener The PropertyChangeListener to be removed */ public void removePropertyChangeListener(java.beans.PropertyChangeListener listener) { mPcs.removePropertyChangeListener(listener); } /** * Put a gcode to the commandQueue of the PF1DeviceServer. * May block until the queue is empty. */ public void sendCommand(String gcode) { pf1DeviceServer.sendCommand(gcode); } public void close(long millis) { if (pf1DeviceServer != null && pf1DeviceServerThread != null) { pf1DeviceServer.pleaseStop(); try { if (millis<0) { pf1DeviceServerThread.join(); } else { pf1DeviceServerThread.join(millis); } } catch (InterruptedException e) { } pf1DeviceServer = null; pf1DeviceServerThread = null; } if (serialPort != null) { serialPort.close(); serialPort = null; } } } ucrpf1host-0.0.20170617/com/ucrobotics/yliu/ucrpf1host/PF1DeviceServer.java000066400000000000000000000275761312100406100261340ustar00rootroot00000000000000/* Copyright (C) 2017 Ying-Chun Liu (PaulLiu) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package com.ucrobotics.yliu.ucrpf1host; import java.util.logging.*; import java.util.*; import gnu.io.*; /** * A server for the PanoWwin F1 Printer. * This class provides a queue which receive commands. * When the command received, it will try to send it to the printer and * attach the results to command */ public class PF1DeviceServer implements Runnable { private java.util.logging.Logger logger = null; public static String loggerName = "PF1DeviceServerLogger"; private SerialPort serialPort = null; private int commandCounter = 0; private java.util.concurrent.BlockingQueue commandQueue = null; private java.io.InputStream in = null; private java.io.OutputStream out = null; private PF1Device pf1Device = null; private boolean runningFlag = true; public PF1DeviceServer(PF1Device pf1Device) { this.logger = java.util.logging.Logger.getLogger(PF1DeviceServer.loggerName); this.logger.setLevel(java.util.logging.Level.INFO); this.pf1Device = pf1Device; this.serialPort = pf1Device.getSerialPort(); commandCounter = 0; commandQueue = new java.util.concurrent.SynchronousQueue(true); historyQueue = new LinkedList(); } private LinkedList inBuffer=null; private java.util.concurrent.BlockingQueue inQueue=null; class SerialPortEventListener1 implements SerialPortEventListener { public void serialEvent(SerialPortEvent ev) { if (in == null) { return; } byte[] buf = new byte[1024]; boolean isNewLineFlag=false; try { while (in.available() > 0) { int len = in.read(buf); if (len <= 0) { break; } for (int i=0; i 0) { byte[] mybuf = new byte[inBuffer.size()]; for (int j=0; !inBuffer.isEmpty() && j= 0 || axis.indexOf("x") >= 0) { pf1Device.setExtruderX(0.0); } if (axis.indexOf("Y") >= 0 || axis.indexOf("y") >= 0) { pf1Device.setExtruderY(0.0); } if (axis.indexOf("Z") >= 0 || axis.indexOf("z") >= 0) { pf1Device.setExtruderZ(0.0); } if (axis.indexOf("E") >= 0 || axis.indexOf("e") >= 0) { pf1Device.setExtruderE(0.0); } return; } java.util.regex.Pattern g1Pattern = java.util.regex.Pattern.compile("^G1([A-Z][0-9.]*)*"); java.util.regex.Matcher g1Matcher = g1Pattern.matcher(cmd); if (g1Matcher.matches()) { /* reset some axis */ String axis = cmd.substring(2); java.util.regex.Pattern axPattern = java.util.regex.Pattern.compile("[A-Z][0-9.]*"); java.util.regex.Matcher axMatcher = axPattern.matcher(axis); while (axMatcher.find()) { String a1 = axMatcher.group(); if (a1.charAt(0) == 'X') { pf1Device.setExtruderX(Double.parseDouble(a1.substring(1))); } else if (a1.charAt(0) == 'Y') { pf1Device.setExtruderY(Double.parseDouble(a1.substring(1))); } else if (a1.charAt(0) == 'Z') { pf1Device.setExtruderZ(Double.parseDouble(a1.substring(1))); } else if (a1.charAt(0) == 'E') { pf1Device.setExtruderE(Double.parseDouble(a1.substring(1))); } else if (a1.charAt(0) == 'F') { pf1Device.setExtruderF(Double.parseDouble(a1.substring(1))); } } return; } java.util.regex.Pattern g92Pattern = java.util.regex.Pattern.compile("^G92([A-Z][0-9.]*)*"); java.util.regex.Matcher g92Matcher = g92Pattern.matcher(cmd); if (g92Matcher.matches()) { /* reset some axis */ String axis = cmd.substring(2); java.util.regex.Pattern axPattern = java.util.regex.Pattern.compile("[A-Z][0-9.]*"); java.util.regex.Matcher axMatcher = axPattern.matcher(axis); while (axMatcher.find()) { String a1 = axMatcher.group(); if (a1.charAt(0) == 'X') { pf1Device.setExtruderX(Double.parseDouble(a1.substring(1))); } else if (a1.charAt(0) == 'Y') { pf1Device.setExtruderY(Double.parseDouble(a1.substring(1))); } else if (a1.charAt(0) == 'Z') { pf1Device.setExtruderZ(Double.parseDouble(a1.substring(1))); } else if (a1.charAt(0) == 'E') { pf1Device.setExtruderE(Double.parseDouble(a1.substring(1))); } else if (a1.charAt(0) == 'F') { pf1Device.setExtruderF(Double.parseDouble(a1.substring(1))); } } return; } } /** * Send the command to the historyQueue and then send it to the printer. * * This function will gurantee the command is being put to the historyQueue * and will be sent to the printer. Of course it doesn't know if the * printer will receive it or execute it now. But since it is in the * historyQueue that means the printer will execute it for sure later. * * @cmdData the command */ private void doSend(CommandData cmdData) { if (cmdData.getStripedCommand().length()<=0) { return; } cmdData.setLineNumber(commandCounter); commandCounter++; cmdData.setPrevExtruderPos(pf1Device.getExtruderX(), pf1Device.getExtruderY(), pf1Device.getExtruderZ(), pf1Device.getExtruderE(), pf1Device.getExtruderF()); historyQueue.add(cmdData); while (historyQueue.size() >= 1500) { historyQueue.remove(); } updateExtruderPosition(cmdData); doResend(commandCounter-1); } /** * Resend a command from historyQueue * * It will resent all of the command in historyQueue whose line number is * larger than the parameter. * * @lineNumber the lineNumber that needs to be resent. */ private void doResend(int lineNumber) { boolean resendAgain = true; while (resendAgain) { resendAgain=false; for (CommandData command : historyQueue) { if (command.getLineNumber() < lineNumber) { continue; } if (command.getLineNumber() == lineNumber) { int ret; ret = sendCommandToPrinter(command); if (ret == -1) { lineNumber++; continue; } else { lineNumber = ret; resendAgain=true; break; } } } } } private LinkedList historyQueue = null; /** * Send a command to the printer. * * This command will try to send a command to a printer. * If it gets a Resend message. It will return the line number indicate * by the resend message. If it gets an OK then it will return -1. * * But each commands has its own timeout. After the timeout we assume * it is ok and will also return -1. Don't worry about this because * someone will send the next command soon and we can know if the timeout * command needs a resend or not. * * @command the command to send to the printer * @return -1 if success. Otherwise it is the line number that needs to * be resent. */ private int sendCommandToPrinter(CommandData command) { String data = command.getStripedCommand(); if (data.length()<=0) { return -1; } byte[] encodedData = command.getEncodedCommand(); byte[] encodedDataNL = Arrays.copyOf(encodedData, encodedData.length+1); encodedDataNL[encodedData.length] = '\n'; /* clean the reading queue */ try { while (!inQueue.isEmpty()) { ReceivedData rd = null; rd = inQueue.poll(1, java.util.concurrent.TimeUnit.NANOSECONDS); if (rd == null) { break; } if (rd.getResend() >= 0) { /* need resend */ return rd.getResend(); } } } catch (Exception e) { } /* send out commands */ command.updateTimestamp(); try { out.write(encodedDataNL); out.flush(); } catch (java.io.IOException e) { logger.severe("Write to printer error"); } /* reading inQueue */ long commandTimeout=command.getEstimatedTimeout(); while (commandTimeout == -1 || command.getTimestamp() + commandTimeout >= Calendar.getInstance().getTimeInMillis()) { ReceivedData rd = null; if (commandTimeout == -1) { try { rd = inQueue.take(); } catch (java.lang.InterruptedException e) { rd = null; } } else { try { rd = inQueue.poll(commandTimeout, java.util.concurrent.TimeUnit.MILLISECONDS); } catch (java.lang.InterruptedException e) { rd = null; } } if (rd != null) { command.addAnswer(rd); if (!Double.isNaN(rd.getExtruderTemperature())) { pf1Device.setExtruderTemperature(rd.getExtruderTemperature()); } if (!Double.isNaN(rd.getExtruderTargetTemperature())) { pf1Device.setExtruderTargetTemperature(rd.getExtruderTargetTemperature()); } if (rd.isOK()) { return -1; } else if (rd.getResend()>0) { return rd.getResend(); } } } return -1; } public void run() { in = null; try { in = serialPort.getInputStream(); } catch (java.io.IOException e) { logger.info("getInputStream() Error"); return; } inBuffer = new LinkedList (); inQueue = new java.util.concurrent.LinkedBlockingQueue(); try { serialPort.addEventListener(new SerialPortEventListener1()); serialPort.notifyOnDataAvailable(true); } catch (TooManyListenersException e) { logger.info("TooManyListeners Error"); return; } try { out = serialPort.getOutputStream(); } catch (java.io.IOException e) { logger.info("getOutputStream() Error"); return; } while (runningFlag) { CommandData cmdData = null; try { cmdData = commandQueue.poll(3, java.util.concurrent.TimeUnit.SECONDS); } catch (java.lang.InterruptedException e) { logger.info("java.lang.InterruptedException"); continue; } if (cmdData == null) { /* status update */ logger.info("Status update"); CommandData M105 = new CommandData("M105"); doSend(M105); } else { /* execute command */ logger.info(String.format("Execute %1$s",cmdData.getCommand())); doSend(cmdData); } } } /** * Put a gcode to the commandQUeue. May block until the queue is empty. * * @gcode the gcode string. */ public void sendCommand(String gcode) { CommandData cmdData = new CommandData(gcode); while (true) { try { commandQueue.put(cmdData); } catch (InterruptedException e) { continue; } break; } } /** * Please stop the thread */ public void pleaseStop() { this.runningFlag=false; } } ucrpf1host-0.0.20170617/com/ucrobotics/yliu/ucrpf1host/PointPanel.java000066400000000000000000000154201312100406100252710ustar00rootroot00000000000000/* Copyright (C) 2017 Ying-Chun Liu (PaulLiu) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package com.ucrobotics.yliu.ucrpf1host; import java.util.*; import java.io.*; import java.awt.*; import java.awt.event.*; import java.net.*; import java.awt.datatransfer.*; import java.awt.image.*; import java.applet.*; import java.util.logging.*; import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; /** * A empty panel with a point on it. */ public class PointPanel extends JPanel { private double pointX = 0.0; private double pointY = 0.0; private java.beans.PropertyChangeSupport mPcs = new java.beans.PropertyChangeSupport(this); /** * An example of the mouseListener for this PointPanel. */ class MyMouseListener implements MouseListener { public void mouseClicked(MouseEvent e) { int mx = e.getX(); int my = e.getY(); if (e.getButton() == MouseEvent.BUTTON1) { double x = ((double)mx)/((double)getWidth()); double y = ((double)my)/((double)getHeight()); setPointXY(x,y); repaint(); } } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public void mousePressed(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } } /** * Constructs a PointPanel. There's no preferredSize of this panel. * It is able to use setPreferredSize() to set a preferredSize. */ public PointPanel() { super(); this.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1)); } /** * draw an oval * * @x center of the oval in x-axis * @y center of the oval in y-axis * @r the radius of the oval */ private void myDrawOval(Graphics g, int x, int y, double r) { for (double angle=0.0; angle<360.0; angle+=3.0) { double x1 = ((double)x)+r*Math.sin(Math.toRadians(angle)); double y1 = ((double)y)+r*Math.cos(Math.toRadians(angle)); double x2 = ((double)x)+r*Math.sin(Math.toRadians(angle+3.0)); double y2 = ((double)y)+r*Math.cos(Math.toRadians(angle+3.0)); g.drawLine((int)Math.round(x1), (int)Math.round(y1), (int)Math.round(x2), (int)Math.round(y2)); } } /** * paint the component * * @g the graphics */ @Override public void paintComponent(Graphics g) { super.paintComponent(g); if (getWidth() <= 5 || getHeight() <= 5) { return; } Color currentColor = g.getColor(); g.setColor(Color.RED); int px = (int)Math.round(getPointX()*((double)getWidth())); int py = (int)Math.round(getPointY()*((double)getHeight())); /* use myDrawOval() if the coordinate is negatove for drawOval() */ if (px-5 < 0 || py-5 < 0) { myDrawOval(g,px,py,2.5); } else { g.drawOval(px-5, py-5, 5, 5); } g.setColor(currentColor); } /** * set the x-coordinate of the point * * @pointX the x-coordinate of the point. range: 0.0 to 1.0 */ public void setPointX(double pointX) { double oldX = this.pointX; this.pointX = pointX; if (oldX != pointX) { mPcs.firePropertyChange("pointX", new Double(oldX), new Double(pointX)); } } /** * get the x-coordinate of the point * * @return the x-coordinate of the point. range: 0.0 to 1.0 */ public double getPointX() { return pointX; } /** * set the y-coordinate of the point * * @pointY the y-coordinate of the point. range: 0.0 to 1.0 */ public void setPointY(double pointY) { double oldY = this.pointY; this.pointY = pointY; if (oldY != pointY) { mPcs.firePropertyChange("pointY", new Double(oldY), new Double(pointY)); } } /** * get the y-coordinate of the point * * @return the y-coordinate of the point. range: 0.0 to 1.0 */ public double getPointY() { return pointY; } /** * set the coordinate of the point * * @pointX the x-coordinate of the point. range: 0.0 to 1.0 * @pointY the y-coordinate of the point. range: 0.0 to 1.0 */ public void setPointXY(double pointX, double pointY) { double oldX = this.pointX; this.pointX = pointX; double oldY = this.pointY; this.pointY = pointY; if (oldX != pointX) { mPcs.firePropertyChange("pointX", new Double(oldX), new Double(pointX)); } if (oldY != pointY) { mPcs.firePropertyChange("pointY", new Double(oldY), new Double(pointY)); } } /** * Add a PropertyChangeListener to the listener list. * * The listener is registered for all properties. * The same listener object may be added more than once, and will be * called as many times as it is added. If listener is null, no exception * is thrown and no action is taken. * * @listener The PropertyChangeListener to be added */ public void addPropertyChangeListener(java.beans.PropertyChangeListener listener) { mPcs.addPropertyChangeListener(listener); } /** * Add a PropertyChangeListener for a specific property. * * The listener will be invoked only when a call on firePropertyChange * names that specific property. The same listener object may be added * more than once. For each property, the listener will be invoked the * number of times it was added for that property. If propertyName or * listener is null, no exception is thrown and no action is taken. * * @propertyName The name of the property to listen on. * @listener The PropertyChangeListener to be added */ public void addPropertyChangeListener(String propertyName, java.beans.PropertyChangeListener listener) { mPcs.addPropertyChangeListener(propertyName, listener); } /** * Remove a PropertyChangeListener from the listener list. * * This removes a PropertyChangeListener that was registered for all * properties. If listener was added more than once to the same event * source, it will be notified one less time after being removed. If * listener is null, or was never added, no exception is thrown and no * action is taken. * * @listener The PropertyChangeListener to be removed */ public void removePropertyChangeListener(java.beans.PropertyChangeListener listener) { mPcs.removePropertyChangeListener(listener); } } ucrpf1host-0.0.20170617/com/ucrobotics/yliu/ucrpf1host/ReceivedData.java000066400000000000000000000101761312100406100255430ustar00rootroot00000000000000/* Copyright (C) 2017 Ying-Chun Liu (PaulLiu) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package com.ucrobotics.yliu.ucrpf1host; import java.util.logging.*; import java.util.*; import java.util.regex.*; import gnu.io.*; /** * Class to represent the data received from the printer */ public class ReceivedData { private String data=null; private long timestamp=0; private Pattern resendPattern = null; private Pattern temperaturePattern = null; private Pattern temperaturePattern_Marlin = null; /** * Constructor. Data is the string from the printer. * * @data The string received from the printer. */ public ReceivedData(String data) { timestamp = Calendar.getInstance().getTimeInMillis(); this.data = data; resendPattern = Pattern.compile("[Rr][Ee][Ss][Ee][Nn][Dd][:]\\s*(\\d+)"); temperaturePattern = Pattern.compile(".VALUE.\\s*T:([0-9.]+)/([0-9.]+)\\s+B:([0-9.]+)/([0-9.]+)\\s.*"); temperaturePattern_Marlin = Pattern.compile("T:([0-9.]+)\\s/([0-9.]+)\\s+B:([0-9.]+)\\s/([0-9.]+)\\s.*"); } /** * Get the raw data from the printer * * @return the data */ public String getData() { return data; } /** * Get the timestamp for when received the data * * @return timestamp */ public long getTimestamp() { return timestamp; } /** * Is the received message an OK * * @return: true - yes. false - no */ public boolean isOK() { if (data.trim().compareToIgnoreCase("OK")==0) { return true; } if (data.trim().compareToIgnoreCase("!!")==0) { return true; } if (data.trim().toLowerCase().startsWith("ok")) { return true; } return false; } /** * Is the received message an Resend? * * @return -1: not a resend. other number is the line to resend */ public int getResend() { Matcher m = resendPattern.matcher(data.trim()); if (m.matches()) { int ret = -1; try { ret = Integer.parseInt(m.group(1)); } catch (java.lang.NumberFormatException e) { ret = -1; } return ret; } return -1; } /** * Is the received message an temperature value? * * @return NaN: not a resend. other number is the temperature */ public double getExtruderTemperature() { Matcher m = temperaturePattern.matcher(data.trim()); double ret = Double.NaN; if (m.matches()) { try { ret = Double.parseDouble(m.group(1)); } catch (java.lang.NumberFormatException e) { ret = Double.NaN; } return ret; } Matcher m_marlin = temperaturePattern_Marlin.matcher(data.trim()); if (m_marlin.matches()) { try { ret = Double.parseDouble(m_marlin.group(1)); } catch (java.lang.NumberFormatException e) { ret = Double.NaN; } return ret; } return Double.NaN; } /** * Is the received message an temperature value? * * @return NaN: not a resend. other number is the target temperature */ public double getExtruderTargetTemperature() { Matcher m = temperaturePattern.matcher(data.trim()); double ret = Double.NaN; if (m.matches()) { try { ret = Double.parseDouble(m.group(2)); } catch (java.lang.NumberFormatException e) { ret = Double.NaN; } return ret; } Matcher m_marlin = temperaturePattern_Marlin.matcher(data.trim()); if (m_marlin.matches()) { try { ret = Double.parseDouble(m_marlin.group(2)); } catch (java.lang.NumberFormatException e) { ret = Double.NaN; } return ret; } return Double.NaN; } } ucrpf1host-0.0.20170617/com/ucrobotics/yliu/ucrpf1host/UnloadFilamentCommandSender.java000066400000000000000000000114351312100406100305640ustar00rootroot00000000000000/* Copyright (C) 2017 Ying-Chun Liu (PaulLiu) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package com.ucrobotics.yliu.ucrpf1host; import java.util.logging.*; import java.util.*; /** * A thread to load filament. */ public class UnloadFilamentCommandSender extends Thread { private PF1Device pf1Device = null; private boolean runningFlag = true; private java.beans.PropertyChangeSupport mPcs = new java.beans.PropertyChangeSupport(this); public UnloadFilamentCommandSender(PF1Device pf1Device) { super(); this.pf1Device = pf1Device; this.runningFlag = true; } public void pleaseStop() { this.runningFlag=false; } public void run() { pf1Device.sendCommand("G21"); /* homing */ mPcs.firePropertyChange("status", "", "Homing"); if (!runningFlag) { return; } pf1Device.sendCommand("G28 X0"); if (!runningFlag) { return; } pf1Device.sendCommand(String.format("G1 X%1$.2f F3600", GlobalSettings.getInstance().getBedWidth()/2.0)); if (!runningFlag) { return; } pf1Device.sendCommand("G28 Y0"); if (!runningFlag) { return; } pf1Device.sendCommand(String.format("G1 Y%1$.2f F3600", GlobalSettings.getInstance().getBedHeight()/2.0)); if (!runningFlag) { return; } pf1Device.sendCommand("G28 Z0"); if (!runningFlag) { return; } pf1Device.sendCommand(String.format("G1 Z%1$.2f F3000", GlobalSettings.getInstance().getMaxZ()/2.0)); /* heating */ mPcs.firePropertyChange("status", "Homing", "Heating"); if (!runningFlag) { return; } pf1Device.sendCommand(String.format("M104 S%1$d", Math.round(GlobalSettings.getInstance().getExtruderPreheatTemperature()))); if (!runningFlag) { return; } pf1Device.sendCommand(String.format("M109 S%1$d", Math.round(GlobalSettings.getInstance().getExtruderPreheatTemperature()))); while (runningFlag) { double t1 = pf1Device.getExtruderTemperature(); if (t1 + 0.5 >= GlobalSettings.getInstance().getExtruderPreheatTemperature()) { break; } try { Thread.sleep(3000); } catch (InterruptedException e1) { } } /* extruding */ mPcs.firePropertyChange("status", "Heating", "Unloading"); while (runningFlag) { pf1Device.sendCommand("G92 E0"); pf1Device.sendCommand("G1 E-5.0 F300"); pf1Device.sendCommand("G92 E0"); } mPcs.firePropertyChange("status", "Unloading", ""); } /** * Add a PropertyChangeListener to the listener list. * * The listener is registered for all properties. * The same listener object may be added more than once, and will be * called as many times as it is added. If listener is null, no exception * is thrown and no action is taken. * * @listener The PropertyChangeListener to be added */ public void addPropertyChangeListener(java.beans.PropertyChangeListener listener) { mPcs.addPropertyChangeListener(listener); } /** * Add a PropertyChangeListener for a specific property. * * The listener will be invoked only when a call on firePropertyChange * names that specific property. The same listener object may be added * more than once. For each property, the listener will be invoked the * number of times it was added for that property. If propertyName or * listener is null, no exception is thrown and no action is taken. * * @propertyName The name of the property to listen on. * @listener The PropertyChangeListener to be added */ public void addPropertyChangeListener(String propertyName, java.beans.PropertyChangeListener listener) { mPcs.addPropertyChangeListener(propertyName, listener); } /** * Remove a PropertyChangeListener from the listener list. * * This removes a PropertyChangeListener that was registered for all * properties. If listener was added more than once to the same event * source, it will be notified one less time after being removed. If * listener is null, or was never added, no exception is thrown and no * action is taken. * * @listener The PropertyChangeListener to be removed */ public void removePropertyChangeListener(java.beans.PropertyChangeListener listener) { mPcs.removePropertyChangeListener(listener); } } ucrpf1host-0.0.20170617/com/ucrobotics/yliu/ucrpf1host/test/000077500000000000000000000000001312100406100233325ustar00rootroot00000000000000ucrpf1host-0.0.20170617/com/ucrobotics/yliu/ucrpf1host/test/TestCommandData.java000066400000000000000000000066231312100406100272140ustar00rootroot00000000000000/* Copyright (C) 2017 Ying-Chun Liu (PaulLiu) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package com.ucrobotics.yliu.ucrpf1host.test; /** * Class to test CommandData */ public class TestCommandData extends junit.framework.TestCase { @Override public void setUp() { } @Override public void tearDown() { } public void testCommandDataGetCommand() { com.ucrobotics.yliu.ucrpf1host.CommandData commandData = null; String data = "G1 X10 Y20 Z30 F1000 ; Test 123"; commandData = new com.ucrobotics.yliu.ucrpf1host.CommandData(data); assertEquals(commandData.getCommand().compareTo(data), 0); } public void testCommandDataGetTimeStamp() { com.ucrobotics.yliu.ucrpf1host.CommandData commandData = null; long startTimeStamp; long endTimeStamp; String data = "G1 X10 Y20 Z30 F1000 ; Test 123"; startTimeStamp = java.util.Calendar.getInstance().getTimeInMillis(); commandData = new com.ucrobotics.yliu.ucrpf1host.CommandData(data); endTimeStamp = java.util.Calendar.getInstance().getTimeInMillis(); assertEquals(commandData.getCommand().compareTo(data), 0); startTimeStamp = java.util.Calendar.getInstance().getTimeInMillis(); commandData.updateTimestamp(); endTimeStamp = java.util.Calendar.getInstance().getTimeInMillis(); assertTrue(startTimeStamp <= commandData.getTimestamp()); assertTrue(endTimeStamp >= commandData.getTimestamp()); } public void testCommandDataLineNumber() { com.ucrobotics.yliu.ucrpf1host.CommandData commandData = null; String data = "M105"; int lineNumber = 10; commandData = new com.ucrobotics.yliu.ucrpf1host.CommandData(data); assertEquals(commandData.getCommand().compareTo(data), 0); commandData.setLineNumber(lineNumber); assertEquals(commandData.getLineNumber(), lineNumber); } public void testCommandDataGetStripedCommand() { com.ucrobotics.yliu.ucrpf1host.CommandData commandData = null; String data = "G1 X10 Y20 Z30 F1000 ; Test 123"; commandData = new com.ucrobotics.yliu.ucrpf1host.CommandData(data); assertEquals(commandData.getCommand().compareTo(data), 0); assertEquals(commandData.getStripedCommand(), "G1X10Y20Z30F1000"); } public void testCommandDataGetEncodedCommand() { com.ucrobotics.yliu.ucrpf1host.CommandData commandData = null; String data = "G1 X10 Y20 Z30 F1000 ; Test 123"; int lineNumber = 1; commandData = new com.ucrobotics.yliu.ucrpf1host.CommandData(data); assertEquals(commandData.getCommand().compareTo(data), 0); assertEquals(commandData.getStripedCommand(), "G1X10Y20Z30F1000"); commandData.setLineNumber(lineNumber); byte[] encodedBytes = commandData.getEncodedCommand(); String encodedCommand = new String(encodedBytes); assertEquals(encodedCommand, String.format("N%1$d%2$s*%3$d", lineNumber, commandData.getStripedCommand(), 21)); } } ucrpf1host-0.0.20170617/com/ucrobotics/yliu/ucrpf1host/test/TestPointPanel.java000066400000000000000000000044501312100406100271110ustar00rootroot00000000000000/* Copyright (C) 2017 Ying-Chun Liu (PaulLiu) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package com.ucrobotics.yliu.ucrpf1host.test; /** * Class to test PointPanel */ public class TestPointPanel extends junit.framework.TestCase { private class MyPropertyChangeListener implements java.beans.PropertyChangeListener { private double data; public void propertyChange(java.beans.PropertyChangeEvent evt) { data = ((Double)(evt.getNewValue())).doubleValue(); } public double getData() { return data; } } @Override public void setUp() { } @Override public void tearDown() { } public void testPointPanel() { com.ucrobotics.yliu.ucrpf1host.PointPanel pointPanel = new com.ucrobotics.yliu.ucrpf1host.PointPanel(); MyPropertyChangeListener xListener = new MyPropertyChangeListener(); MyPropertyChangeListener yListener = new MyPropertyChangeListener(); pointPanel.addPropertyChangeListener("pointX", xListener); pointPanel.addPropertyChangeListener("pointY", yListener); pointPanel.setPointX(10.0); assertEquals(pointPanel.getPointX(), 10.0); assertEquals(xListener.getData(), 10.0); pointPanel.setPointX(11.0); assertEquals(pointPanel.getPointX(), 11.0); assertEquals(xListener.getData(), 11.0); pointPanel.setPointY(8.0); assertEquals(pointPanel.getPointY(), 8.0); assertEquals(yListener.getData(), 8.0); pointPanel.setPointY(9.0); assertEquals(pointPanel.getPointY(), 9.0); assertEquals(yListener.getData(), 9.0); pointPanel.setPointXY(5.0,6.0); assertEquals(pointPanel.getPointX(), 5.0); assertEquals(xListener.getData(), 5.0); assertEquals(pointPanel.getPointY(), 6.0); assertEquals(yListener.getData(), 6.0); } } ucrpf1host-0.0.20170617/com/ucrobotics/yliu/ucrpf1host/test/TestReceivedData.java000066400000000000000000000115311312100406100273560ustar00rootroot00000000000000/* Copyright (C) 2017 Ying-Chun Liu (PaulLiu) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package com.ucrobotics.yliu.ucrpf1host.test; /** * Class to test ReceivedData */ public class TestReceivedData extends junit.framework.TestCase { @Override public void setUp() { } @Override public void tearDown() { } public void testReceivedDataGetData() { com.ucrobotics.yliu.ucrpf1host.ReceivedData receivedData = null; String data = "; Test 123"; receivedData = new com.ucrobotics.yliu.ucrpf1host.ReceivedData(data); assertEquals(receivedData.getData().compareTo(data), 0); } public void testReceivedDataGetTimeStamp() { com.ucrobotics.yliu.ucrpf1host.ReceivedData receivedData = null; long startTimeStamp; long endTimeStamp; String data = "; Test 4556"; startTimeStamp = java.util.Calendar.getInstance().getTimeInMillis(); receivedData = new com.ucrobotics.yliu.ucrpf1host.ReceivedData(data); endTimeStamp = java.util.Calendar.getInstance().getTimeInMillis(); assertEquals(receivedData.getData().compareTo(data), 0); assertTrue(startTimeStamp <= receivedData.getTimestamp()); assertTrue(endTimeStamp >= receivedData.getTimestamp()); } public void testReceivedDataIsOK() { com.ucrobotics.yliu.ucrpf1host.ReceivedData receivedData = null; String data = "OK"; receivedData = new com.ucrobotics.yliu.ucrpf1host.ReceivedData(data); assertEquals(receivedData.getData().compareTo(data), 0); assertTrue(receivedData.isOK()); data = ";Bad command"; receivedData = new com.ucrobotics.yliu.ucrpf1host.ReceivedData(data); assertEquals(receivedData.getData().compareTo(data), 0); assertTrue(!receivedData.isOK()); } public void testReceivedDataGetResend() { com.ucrobotics.yliu.ucrpf1host.ReceivedData receivedData = null; String data = "Resend: 12354"; receivedData = new com.ucrobotics.yliu.ucrpf1host.ReceivedData(data); assertEquals(receivedData.getData().compareTo(data), 0); assertEquals(receivedData.getResend(), 12354); assertTrue(!receivedData.isOK()); data = "OK"; receivedData = new com.ucrobotics.yliu.ucrpf1host.ReceivedData(data); assertEquals(receivedData.getData().compareTo(data), 0); assertEquals(receivedData.getResend(), -1); assertTrue(receivedData.isOK()); data = "; Bad Data"; receivedData = new com.ucrobotics.yliu.ucrpf1host.ReceivedData(data); assertEquals(receivedData.getData().compareTo(data), 0); assertEquals(receivedData.getResend(), -1); assertTrue(!receivedData.isOK()); } public void testReceivedDataGetExtruderTemperature() { com.ucrobotics.yliu.ucrpf1host.ReceivedData receivedData = null; String data = "Resend: 12354"; receivedData = new com.ucrobotics.yliu.ucrpf1host.ReceivedData(data); assertEquals(receivedData.getData().compareTo(data), 0); assertEquals(receivedData.getResend(), 12354); assertTrue(!receivedData.isOK()); assertTrue(Double.isNaN(receivedData.getExtruderTemperature())); assertTrue(Double.isNaN(receivedData.getExtruderTargetTemperature())); data = "OK"; receivedData = new com.ucrobotics.yliu.ucrpf1host.ReceivedData(data); assertEquals(receivedData.getData().compareTo(data), 0); assertEquals(receivedData.getResend(), -1); assertTrue(receivedData.isOK()); assertTrue(Double.isNaN(receivedData.getExtruderTemperature())); assertTrue(Double.isNaN(receivedData.getExtruderTargetTemperature())); data = "; Bad Data"; receivedData = new com.ucrobotics.yliu.ucrpf1host.ReceivedData(data); assertEquals(receivedData.getData().compareTo(data), 0); assertEquals(receivedData.getResend(), -1); assertTrue(!receivedData.isOK()); assertTrue(Double.isNaN(receivedData.getExtruderTemperature())); assertTrue(Double.isNaN(receivedData.getExtruderTargetTemperature())); data = "[VALUE] T:45.0/180.0 B:0.0/0.0 S:0 F:'' P:-1 I:0 D:100 E:100"; receivedData = new com.ucrobotics.yliu.ucrpf1host.ReceivedData(data); assertEquals(receivedData.getData().compareTo(data), 0); assertEquals(receivedData.getResend(), -1); assertTrue(!receivedData.isOK()); assertTrue(!Double.isNaN(receivedData.getExtruderTemperature())); assertTrue(!Double.isNaN(receivedData.getExtruderTargetTemperature())); assertEquals(receivedData.getExtruderTemperature(), 45.0); assertEquals(receivedData.getExtruderTargetTemperature(), 180.0); } } ucrpf1host-0.0.20170617/contrib/000077500000000000000000000000001312100406100160015ustar00rootroot00000000000000ucrpf1host-0.0.20170617/contrib/ucrpf1host000077500000000000000000000001071312100406100200230ustar00rootroot00000000000000#!/bin/sh exec /usr/bin/java -jar /usr/share/java/ucrpf1host.jar "$@" ucrpf1host-0.0.20170617/contrib/ucrpf1host.desktop000066400000000000000000000004231312100406100214710ustar00rootroot00000000000000[Desktop Entry] Type=Application Version=1.0 Name=ucrpf1host Comment=Host controller for Panowin F1 3D printer Comment[zh_TW]=磐紋 F1 3D 印表機控制介面 Exec=ucrpf1host Terminal=false Categories=Graphics;3DGraphics;Engineering; Icon=/usr/share/pixmaps/ucrpf1host.svg ucrpf1host-0.0.20170617/images/000077500000000000000000000000001312100406100156065ustar00rootroot00000000000000ucrpf1host-0.0.20170617/images/filament.png000066400000000000000000000274401312100406100201220ustar00rootroot00000000000000PNG  IHDRww^ sBIT|d pHYsttfxtEXtSoftwarewww.inkscape.org< IDATxy]Uߪ̄$ ! ! $(l (-((94bӍm#vk7 퀍(J aRAeT@ 2OA ?VCR{ y~O:wYw}^ B!B!B!B!B!JKGBx`C`RϿGczqpll7.V{aϕ=K}`IPpEl٣MX 𣏕 ^d`0S w`+``{,O y<0x7Hb3#&`H ?٣/-WE vfo6ؼ n w /6#6Sx 7fBhX 'p0T$p=p p(Q6E#6fqBkbE/aA 6eƚ#("{gbul640!D8x$%X?l B- !*(lw=v2:H1z8!D/ >Hr`6p(0 !D)| ;D1d4LN&>`H2|,lma\L|ʯ.̡!Bi"> H]ؾҢ` ц[5ivg)u!XPo`՚98\t(STUf0;)TϿSXKt`~'c/{= [F9<࿀_ g܋>XP?ڐYU(GcAtbFXf Q"{&~5-r|4" g*vH똟Kox? Ӂ+)I%ػYZwN7_t{"7&c/o֬ZeMQ9T, !{4;̢̃a/E6*lOtع5c?Oߐ"J%`Aq9=6SQ+6}zqOVt[Tct&CԀQUo;~461tbfUug2!*tha2407 ďV px #D#-ˈ ѹLvH9^R 7v#fH`nދLY^Ɩ(G7)5ZKw$7?Ʋ&!DIi:l fJg{Xq׌ui !r$;r-ZO+c){a30֣!h7G_A>щ%6+zMFDA8xoP?cQV:ysi%%wK+1##^\=U⯀_R쭔AyDb6n!~ 3Vw%cx =t^>b_"+;JD7] ~7~ n3 :!rf p{(]俨6?v՟PA?OXdnRe'~0g.-!ďV C{>uqV]@mGgjO"'P̤_h!*P-l{/&DCa kz{ #*`95݊rċ \M Cg6|ϵD陌my)Bjz!S(G1=N=8(edg_3 r cSˀZTl'~zin~M%*ďQo-ޟWr+[;`rڗb9`h#z  |a` tXB7Nl&{D!ZFdad{Tpol(Z)/Z)ߤ`/WS+KzpEeO oIAqS וF̼KET뮦GbExo"3Zo/.4 .CXYQ<5ڈLƲM60+K}"(0;qXtFc66D4d%&,%'O^dц 8f Z݌f`a^tF_O/C*K~LxQG*vkX"!~HC xs>'кQ&܄=F@Z81x:g2+WwciF74Vv hoAtI_3ɒVj^sg"p<s^ZK!vS ૔MOŹ3!au=ؚϰo^~ iG)f З7MKyap4poKδiIjM_;$5TʮJ>C9i&||Z;ERjM ПY Ւ#p@MAj]YKTӱzXTk2lfR{ڽǖ$z1ؚ^gJL_3|6K[: (wl/Jc.Jk%%=7Ծa𬡻b9W.Ē l,F)_pR>ڋBGj=IU`=;>9_Sѿ!X/#ӛS &?g*W-mDf(G{u pI쩛v `Xk5]SseC`pG)d#tST.<{q9^O!5<.WpzBQ'p^/T@]!҉-\8JW! ı8#oH !#}<1؎ !G/TBTUXg`3.2rB!De `j>6K*PKX؉(&<.N޽Q`iY<{s3\g4%-0X.!"ZLy!DVwcy`C_q̭$͝?OENyZ Sy!D^..~-DV%Ô( Ӂ?G}~ħ˔Uجha^4f`JfRuFIR= |;\&:ĖQ!ZYkNQK^]]vD؎7D+wC]^9?<[',QJpwI,/*BSKUIlNΈCTua'Q?'rj)muOF+G m*`G࿀1ە2YiT>Dv{n-|45k]  `[ğSF"JE[6Vl_&|,Nq斏 Eki,3]P`s:E}h"4%ꇈҍw줦h=gB a>d%7ج]d%/ fV0˚d0`ymHh=2QXZZwov X+Jv%цo+c%6ü5ڐ[6QH\{>2t\mHE3Q8܅+~mDy ;H!P6Gz..7 ^6DZحLp߫IFԌǰ5*Fs-J#Raժ+f-"O7p,J)瀛`.U,'cӬlLCQ-#磍ebx嗇 ;u gH\rЈK3C<-'ˢdZ"*k^ |61 ߈6B]^mhȗQ: EQbP'F!Xm#—щ2p@"\41h#DSt_6Be8Tp+(@x2´fq7(5D!2e+"aDPpM(+E!3_ԲL5^f.6@$g#`f~Q3zPnnǿ$ϱs/;h-FIB>Q*^E)}E?xoLŞbڛi4q֡Qp)\xBjC3aQp l LxZ#6j s4Dˣ '`wo}qt7+{4XП3ixјs(?D!ru't.l34.!>gSx 45s'w`ע" {hg)k_Sv!EC~mȕ?`#9]+kr_L;NXP W  e;xGw6i<ߪ/*m-hduỶ6B M- (/{ߩĦCok>)GCDxXmȝ]ǭѯ`[>5shϓE^b-p hb{jgZ]Yd/;cO(̍6@{聂{v^w~}TΆeշSCC܇э Ax0`ӵJafդo24.hͽ3z׮"(L%k.@7Q3˜Z ^te܇7z{^fve^mԿ{w{Y \mj5Eq_F _iܵ,?O6x]E,Sq`3kx<:^v:]X@Wcu_Gx'_ԲLB~ z^3mQp*:]WK2ٙ|!mw}K7k:]՗휮+bqǶܪY6fnL~~>p~tX6qAv6tB[<Ȃ痶W*"')Y\-DSz^}{.JVQx7iN^j=w?^Fe N1+)lp)lmx]{$ln*x}ܳBeFܗa]>G1;ؒSNמt]Î]\kWϙ{Sr,s 6C;EZTp,^%f9^IWlA3x8 {^=T|}yyY#DgR o=XLwGP2s ϱWe9^fq>/Cdp&l &koFˇ߿hi~"A g{Pj+ӹ"rdw|・n\_ރg0v#nںj {^Ε~rBnh@z6GW%D>uLMo ]y?vC |Oԉ|9:Ziw7;e'WmCw|;76ö{+yS=~YAI`emBf㗳jtz~E~ tׯ2mG/\G>5a_|gk5ɼ0{9^BRa.~}saCߗbUkY ţ}ؚ8`+7S5OCkW?fnE lM_!y<輍8SM$֫S#cV/JPE9p4vf,Z!n୩(W7 qX047hsHӍȴK'2ګ\h׀{Is1=ݰ}rᝮ;Foh׀% ]"ĚÞRǵϧyћ{]acSKҸ%p!iw]"wq- hGy]qO+ 8t}D>U"h/u4t[nŷ0jqҼ~] s {7פH՟ǥqx& 4~ZwokIgcAL`1ZT?7/nƷϜxc0`/=S >Of^A)bk|с 8J =$cv컏p&o_]‰Y3T òFw]4p)$Շ_M|ߒ‰ i_q`z AyՄ*|ͩщSR9Qa7;WƑR_F7J0k.L O nK)пotX0ǷO犽asothQD^ ndqN$%ʇIg^e9D|i6ڬjޏ|=W :G{#vtbĵ(oGkʝ]H҃Z4-&sFlpƶJ"oDe,x3wJ*g2 {ԇӹRy:໱GR%̀sbk9>+7I[ Oz(U-pݾ_Ox>#0U-A;f8{[ѓ2/쎽L}C7ߏJtԆQ5z8\Y8cYu>z**ߗ{'IGCf}0nkhQeĤxJv7n*/(!Àҝضw>dV7xmO?9XrvIDAT'fFH\%Nf +)51vt`Ň‚ll(Ua3Ҽ0my[ u]r^Ķ辂_ña_c{= f- {wER#2yz8V|s|vMJm 3Qihu_!AeHS+jXZJ/gΕZ3Fz8Q WL>.\Jsv!{d.+c fcGXKP\5g_\t@lK8s2iTew9='%:`:JivN ȧ|_wK!X@W'= iTh@?J,hbUY/'bD<,WɊ`ʷsucOtVcya´X|4O*ag|ttetU72HҕD"fZFae8_)WHQ;A^"}61<0{1T]]\F)D>05hSD&b+T]7*J4} 2K |ӹ"22 ZoGbEz"]?_ȧ\ej[ҹ"Zd8Ve58cI;)yK{;ҹ"d&5 p@ -Z3GVݔ'6·fc}gYp+-23i']t8 oF] aC'6YL՟|fjC;gpFh"_>I~4ns! t'&5XB[bR<*ts:VݳZS6읖hNiW0ŷ>3G,˨ǰޢuRգT^2FMM]u=OJi ^xw"3ӹ"rZB%f;d ݫV2zB l_kN>:"g|"+ : snHwQ-5pV*gDKn'>VUPv93CspoC>FSU׍(mp:HWp(Ķy6gy#7 ]SZe lWN8F]F Ş^ VUBunm-߆(jRJkj!SU", 6joZny#`,at6x]UDB}|x1u2o@LDi} fwL=F<6 %mu^[ǿ?ї̤36C-8{ˀ7rH,nLXUM6~hjt?IP͉ȓ-eWoRjf}l̛z} YOUI< 3qKpN;#)b[Լʞ6x(Ii;FJo"tQϜys76xD_'pS73.Z;} my?%6&mΜLڎ#]fIS3iOxi^H;VUc٤QyuH9nÖ,fpR?mhil̋2A`j  Nzo O\.$[4໰Jܽl(^6>I[|1CJܽjoEc?`$UI_Gkɻ#}1[&&搗$iZ*p58^:$IQB'OE3Fуr>p_O<g7p0=,x>JRZXb-6n'~vcso&Iyy1c?X{Vg2=+? am_=mlw:Wt3.u)8eIޘѶKR Fy[% رIffIK])pȁmуzA~H:vJR^Z9.Şa"/6IC3lb~S{$)]DۂDlvPыL.6DvцU'"\lv6/Z<8x QoG%)-ŖajUyBd7ڐ*eY\eA1(#{7$5rlIqt 7$jtKm.p"*j5VGx>%lp.G !\-ūev!2zv/@N6DξU Ss'Bmi^B|5#!Jxlxz8 !Di_">HqZ;2IsIZ9p6@՘\ \}wǚ#Ѝ]o  lh+P@}Ppl !5G cX@tw1JbED,KY =3QtPta `?`DEq  "+c/bўdcf`չZ$Jh4{`3%,Z$*`K`ңHy ߇%{xK+ "A0 "gVn–Qł=?_3KwQFs3,o\,J=5L\wQ6DðOac{$z=~{ x[/n!B!B!B!B!($ !KIENDB`ucrpf1host-0.0.20170617/images/filament.svg000066400000000000000000000055741312100406100201410ustar00rootroot00000000000000 image/svg+xml ucrpf1host-0.0.20170617/images/info.png000066400000000000000000000060211312100406100172460ustar00rootroot00000000000000PNG  IHDRww^ sBIT|d pHYsttfxtEXtSoftwarewww.inkscape.org< IDATxow]r+R31M!j0n$IX$8AfFh"37*JF*ftnsv\ruy]y<[s^9SK񵮬88x/rqzaÎvUnn E/~#8`T>C:߼۪WaیGmL}rgꝇ=h[q`q[:`_Y0\-J WUoC!Oh?c}gꖃw?l/t6>U]}s8S;þs6a8*?x'6'*yAxظ*9pc|.8A` qHw` qHw`V;ϭpW_=|NzPݰzfKW`{ݱz\ĽV`{p NMzp7Wo^=.?7Z#7GsIOV?PMaTZ=ag^0wdwdk\z\Hw` qHw` qHw` q]!-Ve+[RG,:3WTx {zm#?~jo#`G]Zor{_V=`aUoX=s{ܟz QGp} a;zg4lO}V!>zgcVl|8=kpPTZ=s{kw-nY=sƻV>x OTϫ޲zag^![8t3rn]=\Hw` qHw` qHw` q]!ϸ_ezQ}5kpY}ϫO}cV8xPg_~c(XEIm~j;خ˫_z-pTOꙫGq#8RVׯm8U^=Ӿ p,.z}V\z}m8NWX|p=oY=##8{?RݰzGwW#8~!p}V?Y-l]oWOn]po3~z[67jmޭT}vXJoN?v2;@0 $;@0 $;@0 $;@n{Gp}!p>~uX|3T^u}[,.l\_E},gze;]z!uW7~s}գWg(T?zFʶ=r(q{ƹk(q0ʗw%7VX=1Xݱz^R}ev?UY=n^us'߾x믪&b 뫏.n|3k۫W~\U=dNV_Y=.DTu @0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@0 $;@p^z\z_s} @0 $;@0 $;@0a|:;|.=l?xpa8>w38􉇍] uk}6>-k׶pgjh0wU߿^Q}\![=nKS3m} so<}tg7Tom{x&zNmk瓫+|>4wWon߷u趮U UOvK>_zWucw[uqFWVWDT_\=2 cIENDB`ucrpf1host-0.0.20170617/images/info.svg000066400000000000000000000065101312100406100172640ustar00rootroot00000000000000 image/svg+xml ucrpf1host-0.0.20170617/images/loadfilament.png000066400000000000000000000325141312100406100207600ustar00rootroot00000000000000PNG  IHDRwUAVsBIT|d pHYsttfxtEXtSoftwarewww.inkscape.org< IDATxyeE}l0;3 PAq$5&[&$*5jT67PAq j" " 2̾go[uΩ~=O=2{:u$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I6tRO=a4 `:kf+y)d|?׶_w=.5c?p00gϵYZV6 x8c-pm<88dσ%mO0]nӀc<0}R?kKV`;^Z2WH2ܥGE#od^wo%uYN#\|\cr[Iy#I*jat Tm [)IY 9ð8xNKjtBȜKtHӠ7Jbk/78[͟>ٛ&I{mC4ũ.WvjTT- D{?X%lX<2 Dzz_c%akٛ^Kn8XUkI MwG{n4ZHؐ맬۳:5l G蟵퉄m Sj6=#{VP~Zn݃-go ElfARlr8#H %oC}ɒ*u4”GfO Ih a,@es>CiTKv@w5CiuIM|᱒0b=ؤ#= A:%uq [wk-i2$x,)7?v͒Z8=M29Ok?k~>JZe 'h8WM %_J'4gFgae ń }]$j­M: M8<)az$ LǤ9N9pH#znwG;9, VHjӁy=69q\M#%zS ǧy'} p1})4%O+>HMُ)YXC\*l6ʟ̫ ('v?*dG8Q{])x-?뷥'xI} q>yom5_` KSrϥOڍ o*c ’ҟ5wRD-`W6w$ʿ?7vR=p :Y gPJI#S .}bj kaKNxerfA8FzBꈃiTFzBy?Q?4RG/NErQ[WW&jM%(~6~J.}4yh"E;k5,O6o4SNOI7R+L;kҎN;湉vjMջ)]@60t | PE^F{jMnJCϗ(>7ѾG:P`O&A Iڔnܵ_xp^$~ ?]%jl.ЮMq%BaKFWg:Zw*7Qݥrj?d+}\gLtlr[%p?]j9RO3OiWd}6#O&G.;84]SR~Aý..Bж t;9Pe]c.#=m5WT_p) P?.nn'>U,r T85{G҅]6w<޳&lCxt{ >W=Te}>G[}}"m]oL0mG#^VRolv`B[ pѳ#lXHkg'Qi B®]$Jm4Rv[ި3nSw՘(sݿ@s=K-&ێ[ x ǝπK"^Pw G}lNSqEMܻրiyϨ {-)]HExd{=t=sϯ)]HþJxt!=24ܻmK|gNwn+\G.t!=v4n|Nw.$ˁ'+/ýǢ|MbF\WB.^MЧ| ;t='{ lK3;PL?noS*ʿ'}j'O#څ]͙K5_2%҅!pﮑtMJॄvs3EC{OhR&쵢G jK rޜ{ {h|kJ[Bý ޙ9Մm ԬiGy].R?>[x?FyM;Pz#<~NCx\(/2ܻi7[|t~ܚisFyM{.R$<}^Sw ETΑ{8rOo%εTn(/rGnS }R.u.$\؋_{üp=KPsJa+/UW- !?uzmWRvnE{7X,%cga_`wL3.O䰄 ?ߗ0#Gc$Q#_ aM,cEXͲ/Eˑ{ &kRN`ŋ}4䲂u+ࠈ3yMLݳy*sc* p_רqml߸ݔr8 ٹ0uFP˥IhpznwOe$ <04d?.E2'- *cJ={yq:1}m< ޸tcn@ q/O'ppu__bW5 n~" ֱ="_'e?MPs3_O])joੑ_) i9mtks3_+>A{?$JQHz ݖuQh Oҭ s_^umBR훻%o>d)aJ@_MˈCWmD٫gaXL>Fs 'խߓj~>9'{5b*ߚ#~3{*k}`^G}\#DZ1u4߫gp1WG{k;d{Od3f3>m/׹| J8"i p#_t*_ǚ&׌8 Fw%g+ Lvi/!ܯQ #>XU,C,ȫ>K|!lz .&M|!¨=^jyI84W*lݤۮx3{:#}pjp#҄}-]I~}sK]ֵ(u>&'8g{v#MџA|{oRFYl|C?.ێ6U_:'O^y}N_r6O$̛ [~+lF+U~nY4W/^Z+X9tpA/6. ]=H_\N|횽jJ/9fvOl |GPΞXy)8٫$҆7yB2ۍ[;GWVOI<&Adpa5K~/oYgҪ"tA Kp|h+Ťk/r´)}PcSX>W%Dm|pA~8wj-0{e#o7w(ѦF/V{*ޑjZZ/p_>O=t[4lVR7O"Mp[Vi6!x}m5lG.$n^ZoN^uYـ_CxlYi<3}fu)p>.\pq//iGi>׶O0 L|t~MZTc쓛TػU[dwEj*{l8v#IүU hp-xSTۈ{__J 7/'/PUƱ?o"ܘ"57mI/p?d7mqaXDNWq_rk<|#.x [utp-"80w- i8v̳HӧW.\ݳ0q^uL#,-]N mS[ |~3HH.\ >hך<چ6xZ\wV'Ճ*w?^u;"R>`KQ1M>>3wg^^V{,p6W[I`W&]_tuk=%{ǔߦjtYufn:\q!mObamO)MOL9ՙkW%K͵Iʇp+x}mHӯZ,#اc<:y1.w%]+sY}WPS:|rv#a˃:p>%yWm ~4Uwߦ ImhJ7I׏[jU>K-cP>S}פ>yWR{JoJOSvR@FIwڕ(|4M?үd;iO'7GJ.R>p a[:lg~;_~FOUwזqKoԷՓ_J~sԮL(fDj>Eٮ![Qm[H?g=Lj]煔ϑ_vzꝝ.]xD u7f=[^JXIG[-~/# 8u*SYlB }]xJ`mKڶNyIh{.|}ѯ9l\NcN΃ދpzBx]F mkkӀ/g<$c?o]x -ry7_<ico$C~oMp$iovLF'ET6mvyKò.{hǶ"ʘ5*Ca[c?ӿgN|Xv-~kNh! N|ߜKVFccӁpz|@rn0XA@j}/\{n|(΢ {6x j~)ʫHAp_~aXc60 L4Uĕj94i+ڃkmlX/RmKLO=#iw)IA DJY,"͇<7S>Rl|$p_u~ F$8nAbʇ^2gp܂A2 Uҝ A7oraǶatuNv.U6~D"TK{;mZz&kYN@꼝>:9fe==Ҵ 'x NPIAȕOԶ؂fFۆomaUV^ GڭmvyCOʇmmw~KHz5iO ͉1;3}KH{O6]fkGv)%=Voz֎v0G te C3/2}Ip[H=6 O% {aWلuMh_ σPn fݭ<s dkwvZjo焽8yqzlk KJ=/݁뱵-~I|2'55k6 fZleM!HJ\ʞ/T[p*5<ʟ^>=if .pJ.^DSvr2p=v0ܕV/\KqN(]M0 ssB‘r0Քہc07]98PG6IbGMRj1x%p wo~ O$ wk 2}("uw C ª `?ke8Fr\s;R2o`[+[N'}>>M ¼qZs 7 Myu턩n;IDATg{:2j?\^l :'l{6)SASoʖ}`㥚sz…3qN !L쇡A5#x}kW{}WZk_N$,k\Q m,rZ x-p]das q 1`N+k->NDt505wjA52JI{1h/$'DT+7̑{BXBX>y6F{ser`52}zg/&0uwG[PkyA5^eX8uRjE[pix] -d]yӁF[ ?oj9}0eu57vؿM9x*pIҒ?"̽ ǫ%7vXؿLXvy!о wt쟯婢>sj $lf68w>rX؇{f jXMX?xЭmcvz`Nċm+0Vlƺ f]oú~\85W tpeek pXT9rgK9p23sZ&.efP L<]p̑{<]̠̑{/3j2 w)3j0eRp̑{TRf`N3ܥ gK9rgKT9rRH)3}0/s.efL<]̠ix>2 w)3}0eRf`N3ܥ RH)3}>ˑ4j0GqbpF`f1ܥ cKT9rcK9r_F`P =#w}0=.`L]*̑{]*p̑{]*OAK5r0gvYgpFЗp? 8eƆׁ|}Ŏ] )/30 "U)viہoUcOԘ/ A-> 4׮O [IMaFmyt? n_.yjilN ,hc;%K7t u> 8v ˁ[;7OE̍G˜Ը+Eƴwf86ځ~iյ,}-Ks)T@ R2>r)T@kg[8.зpS3ˁ/gmbK1/e~3~1ܥ+{q߫ w>;σ2}2ܥ7We>_6R} wsaS2]RfKi[M{׏/Y>? G]*N|%T@= |+@p{>B:T@ӘRZ kvK! p~o&z]]*pR^XuJfCbv Ia`̵݁/Y>G|ʙXK|8r 0I15̣R:x/'&>CU*p_gqw3T L|H8-#`oj_w}ZjaKv9;y0ܥ G;0>UsThkJp 07̅U׶OΥR.[\p-]RH}|˙>_6\K9-#`Ol*S3N fK $X8T>֯X3ܥ ɝMxlx]*pܽEk2U>CU*pl n70uܥ ʆkW5pR`pK; ET>5Y.0t-JfXT>u,]@GRN˨i. 04GRfKj.`iTᮦR3T w5gJj2RfKj.`iTᮦRRH]Ms)Tᮦ9-#`iTᮦRfKj.`i>CU*pWKj.`iTᮦRfKj.`i. 04GRfKj.`iT@쉗J!)o+]DKR wI.I2%B$Up T!]*dKR wI.I2%B$Up T!]*dKR wI.I2%B$Up T!]*dKR wI.I2%B$Up T!]*dKR wI.I2%B$Up T!]*dKR wI.I2%B$Up T!]*dKR wI.I2%B$Up T!]*dKR wI.I2%B$Up T!]*dKR wI.I2%B$Up T!]*4tc(]67҅$G҅h?/]'qM%a`VZiM:E$8XQIjP~%I]vkꎑiMf5/oW->RIj^z=j[ 쒼WsA> ,+]Ȉ)]$ބpQmCM`{جΐZCWֶKKОj{I4إ{*^ڳw$,`>{P[ >:j`S(ڽ J5 ؞Ĥa=GBࣥ.:ޗ6wTKm5pA-I;`.I҈wP>o3AyIR} Oc$M{$HxaȦ$~AS2[;IZH3758$Ixxn$m==Sx$3iz_| HyMS"Ks$Y%I`_!wᒤH(&ܗ;.\4imyc;٫$ FFߒ`pR%IlVn4E\>,u/:IR&{3MMO.Q$iIO5}>$u1L}}12eJ5`B5J2 $ o0R_Q@Ihޗ[N4As˕&Iq&xV$I`||$u 0k}Iφ{/,Z$)4օ eˑ$Z}!µHٜˀ "IJ3OJ!‡u-)]$)-%I:z<{KRe~tRyAUmEH]gmV3K!u᮶YlRIRZ/' image/svg+xml ucrpf1host-0.0.20170617/images/preheat.png000066400000000000000000000245451312100406100177560ustar00rootroot00000000000000PNG  IHDRww^ sBIT|d pHYsttfxtEXtSoftwarewww.inkscape.org<XtEXtCopyrightCC0 Public Domain Dedication http://creativecommons.org/publicdomain/zero/1.0/ IDATxy]EowN@20Q@PD((**z_/﫢}.* 2,  CQݦ}ZUYd:{U}VSvbm&O(DDD}zs6M,^` ӌDDeaHD+8,FDDh7͌FhHL3(EhHi̽z:ӌqEDil("R-'fcğ>8]*"R%a[{RDBf/`_ Pq4[NY'jH)?9cp Y^`9"O2eu}K:3_o6WDҕ{> kZ]$"2|:AN@JTt0:ATED0XZRt^cpS SkHFJlIWe9bD Z' R5*euuhHF$2rhDrmN@jT0xuCPqLŽ sLŽ )_ED6\bR֋HMrR:Qq_C2ˬ-(DNBjt垷7vam'bB=o[' ɈPq[]W"T5:N@T&!>:*Rq׮ 4("ERqW.b@=O]l$.b@=OIɁ~cހHQTuM+"{NI HM3C!rT2ԇ/*iڰ4)*yYa;t)j~˂{^['A'.$d w7d"⇊{^JrxuF;瀭B%"RE`?Eft#ͽS=b=/% ;:n`&ͫE&a+cv5N1W+@#-xË,R;/>6kI36؉8? lIToN_:"uW۾v+7} ѕ{~N -Y'QA3=y|-i{~J,_&Z'Q1~_Oڠ➟RIT̻=#_S2~u'zmv8[oI-3qGz~)"eY}ݽ va߿zZ#R/c.0WɿM-@PImsӃ D>Kpo'B ?"C8_:V yxߖ4Kjѕ{pZ'QS 1ұD쯨c* /=W]%vZ !r`oWAVq"y;)쪕Ĺy+RK/xoa~%|P] W%:q;TuuF?7#e?~ v?D +n+"Mݰw+~;c2:"҄9xS%1uga^ B6ZDbM-=P5oifu-ujު:cf(=La.-Ь TOinV6)p q.!@&[' N=VgQV{8)a0 R*.CJ+ixl&";oN ر6ҁ{(ai=7X'N47'ŭ&xm:-mHv,XiV|=$ Tҭ[[x 7+s"n}=/ pST%"2܍ep=pp?p0L6í C_lNٟsC,_Pqe,n<ִ7G]+Q$`&nj?sK*h%7ENY|opƶ:*8mnN@d*Tpn8@$E:7 a%zt=a@o ԡn@Ž*Y'PE%ϭ 3g uUBPmDR oDZDn}^&KjNJ,oNˆ TUi},VƕEHiq5UQ/nCT) U^M]R"(ONkw{7qkgrqJR PmUu&X'!6o7{q8wN$19}Z' Ptܚ*Hbrw J*h0S9={> \'sj@Z׉HX'ф[0E:˵d?zqnN [➮0ğ߂TϪX \d89 -zcU2:}ǁ7H:NB{+W3}'Ǭh^pKP\>i H^n3-Mf؏NƭMcoc,M؝O6=m:bY~@Uk}$ԇeOvKoYk nx:qcX 8\:2|lIWSgy_յchk'b \:Bp3e dnum[sQ=_IGx: yTDP]K  ȚR- lk=FֶNMZj2[#5h]v;~mj=nb'ο丣wC*=|[~[|oĔ̇<._/'['--ۇKGuR[j},_M_/'[Y'Q;7o]i?w6c1npR+6~l<ڗ΍ zEݾ9i$r;Œ  1Y6fu;J-bi,Dr1|9[X}z藉 %m^A]4,s Ʊ#ư' GLm5εESiU:ᅦ} ށ_{&U 0x׹Žipe"9G!ht}l[2{hOP }]Y=Pp\IEoy鼷Xk<鵪b(9wD8F'n#($AspW_ҸSr{AE@jK=8q \R҉P=g)kFUnN 7wzi~H4nĭ@}jAfB9X*܎ ;\\2T }M[Ÿ>nn\p|l)KĵqWr!mwc1a&N~N~;(GS[b¶p\׹S}q><@r&@-x(Gac W_РX['|Iߟvo8[rH`H&Mݽp7M PqoO7[HAdv_|Wщ8kׇIghVS!K)Ӱ#'Eڊٌ7*ziìfFǕ2(Op;۵oaQ;hnJd83iPC֊ք{zH}*mG% w~^}j R;1͢]Bu'JZd@":m[HTܥܹXX)Pqw6-&׊f4*K_F:V z#y'e6>RDzVQjأ l+FK)G< g)zJ9vk5^#fXFCit!4,$4ӔmCKODw )[7j/ps M}k2-*RvƪO]^c:iX>w%Bqw "ʫ0*muMŽcwǫ.['`uW7w)Z771-6N@p+]Ӫ$1Ǭ ?ԊrWqb_\lpL N_o+"NհĢ,48nl`FW婣]E>TEq_\ap\ N zhJcQ'b1pqc;:v ڳTܥ$WSL+Em6Z4zqxR-ˌ1cZʾQ.P옻%%Ǿر}xuLN| ]`ᱯ7Xc4u"Z,WR%0=831\KI,ܡzC3>K~co.$2QvSqC9X[W88MY8?|,Wa<x8^NRpn6MuL܀[Zc"O#>43Pp4m"D{$ATTq]K)da@F= 7H`Zc7Lv,n(%Qc$SqPR)N qf 7\r0mʾꠍrǍKz']JQ:Lt;Ey}1X܉+lRM/&E>/Cn6 A,M{_ǔ"Y*X' I$Z'PAsHkѮ{1cutu]Rnj !6MU(e.NbRO]R{'HTcZh Ҟbʽัtݭ?6H=onXE=c #%։TCWRO^пnL^IQiM/4s >(-HeJI|"HgUo5:n e"Зdݿ_C3?Nb*RKVݎl=pU"oZK-7ZxދKuPqZ@Vc0bJ7rpuØ"2P%U0ˀr)/pɓ欤ciJ~tK/nhhFzHmqr',)Hkc@Q@,W? kމDz/['wژ.X? ;\D^H@*Py;mquMRq~ 8SJܒ٨/167*.ڎzW T,>iD TܥձBqXegCD)/X'т?_~BqGyfg{Sh ǀ;b4e.091X/py?X.`kTyea93%yXw-Jqf W/N$S7Z'၊{-jqT;9 xM$֮g)c=0iڥTX'+i{M'ᔳt [=~H 4 QJlD_:rZ;Sa_ܗ@.4Ѕj 5YmirOzG"LגJiX.:h@W$J{G['H5EQVVUC2O\_qY}N8u`T}b1Apc oX nb݆;`(žcŽ'Z@^om;7W~xBmgK::F,e)k'WXCj<#f_7Wں>2Pj8:+vw~NMWy$B܄}߇iN 3VIcuˀ1c$nxW?TD&{%_u~A#Ov-kݦfbn *s Cx`v@~ڍ{nDl vMOiwI# $ߴvnl[6غa@Gؿ/b9ne8g'kzpo^23vi}~>#z67F` k7bL6k\SJOP{c=s9tf7'2@7{ ܃:UKV枼\'cOa?v?B7;Eܦ <0p!m(HOd[wL <0~"]N =C),`;B݄Қ͎+5Kuz"Ջ}U{bАErIDAT1;sfP匃87H~8\܁IߪhCسqˊZƏwdh&0^E{K1O"nGm}#C؅|OD2; 3.ж=hKx桏7nf83?@{BEpI`Fb^?M'd\賓?&^_f)1|MgItN{r]u!Y [s~ܾ>5t!>;I \JhZԲ3n!%;vKz+1Tl=$ i}4R qiZ{ 7 vN} %wˑ։4i܆"Ùl -N_t0.9`b{"n9։TbSSq9)qv 5DGDr =Ү[?1Ѽw7,3I< L7ڸ?G>D#>mC^{>FwExV<^,i4N "Aubi7Vf@.ɳ "L7s4vxi4.lcVx h D v {E 'R#7G$eSkB8{cB5@3`9i'>caBuHv% T؋:/[ oh5 {>F;܅n8)@H!"ٝ/CR67Th6<累D*m{&f,m bA"n'*i؟ .<"l^3$OkĬFkȚ"isMBtw%hscGҿ58.-+R7 qx|x?lm&p/q#qqnq6dhFrz9%`dnyNHm+{4pqM>t\?Y=DŽm0`)P <0*P4l/!?GtȚ:b~7 "-pU+;ls%#?b^Fm[᧫qvx׋uEjWܪ-U7zq s 7Mr< L#aǁ!)@a|zЍ*x ^+EKFn=͇ s/bnIZ/ǀ[&JD$=CHu֫;q?t+/1m\}Kq t y؏vKa9" o\"2w6rB-/N? N)": ۪^?tf/]Fu 6 <)hܜq3|ѹ"YC?B=w OqCj;ӣҮpŬ?v=|3Mhp(p0kqgK]BW} \ s 쌡sqEA(*"we-̜b?. 5'=7`},X<<9 [c -Ȳ IENDB`ucrpf1host-0.0.20170617/images/preheat.svg000066400000000000000000000067371312100406100177740ustar00rootroot00000000000000 image/svg+xml Openclipart ucrpf1host-0.0.20170617/images/print.png000066400000000000000000000050251312100406100174520ustar00rootroot00000000000000PNG  IHDRww^ sBIT|d pHYsttfxtEXtSoftwarewww.inkscape.org< IDATx_&u]$qB$66ڋdh &(."\*TPºVچ9{Ŭ7?fz)UП>tqN0mW&y>; =]3(+l&y[jܗIzܐ~to{Zp a*HtOTCpK>5R:zg6ϋ StJ^LC\ \$״GNw>˸IX35IN &N W7V{y}񦦿}aY"_H/ {0=֑Jޯ ;}}3/$o%iyI>%tlYUzۺ"əoq1`|OeԴ7ɷZ0uק||_cDcN,^/^m3AJN7t Jz,~}+o;{2,EtUz3ۛn+$aV,$2ͽ7ҽXD}*F xݓlW02.`~iaO`9tV\>I>9,^I^7,˓z "B$;@AP$;@AP$;@AP$;@AP$;@AP$;@APо,In=c;t,CKs-ʮn=j=N q(H w q(H w q(H w q(H w q(H w q(H w}ءHr,|zAؕ%yKeW};@AP$;@AP$;@AP$;@AP$;@AP$;@AP$;@AP'tIENDB`ucrpf1host-0.0.20170617/images/print.svg000066400000000000000000000072001312100406100174620ustar00rootroot00000000000000 image/svg+xml ucrpf1host-0.0.20170617/images/settings.png000066400000000000000000000251211312100406100201550ustar00rootroot00000000000000PNG  IHDRww^ sBIT|d pHYsttfxtEXtSoftwarewww.inkscape.org<tEXtAuthorSonshine PenguinDvXtEXtCopyrightCC0 Public Domain Dedication http://creativecommons.org/publicdomain/zero/1.0/ IDATxyU<$s$@eF_VAA 8/ 틊-*y< !!$ޛu.wsjZ~Y'guTDDDDDDDDDDDDDDDDDDDDDDDʣ;1k`9 <ᘗHTܥ(;v$`bg)"F6`0 f}@`w+XZd*6X|0EZӱfywXքJ>X\ /"R#/wGӬ~MX\_XkD&b c7>c5cZ~MGqN,Ix$.-] A'>IN񱨭|nS]/]cϘ*n64L[B]L~߭aD&3/<=KI_w8:bEz{Ȩ&Z-~T /0}YZ:__h7_\Gk}1߇Eu֋~ ,y;Nw{'!a˝ d0;^N US1*2X@NV$,w3,o-N vp pw,N#ȩ/>ᝄ.`״fMі 䴉w2ИT]|3f#m݇-"%sEk\>^p;5?'(w;6g3L~??}:8|~|;N/}ؓYTdEzpίp4J%z*F;6kCH0lwΫ?.R3њ"Ehb~N=mY u+%""kjޏ-W:!wE:z'W.M n63)e]?]_R_*"Ҝ8މQނ}Hy'"" w"ͪÙ!Fy;f`w"Q iF܇vWDQ%މ4eơ."[ wͨzq_NBDnQ*"ҬhFՋ;؊s{'!"Z@3P_$"""QmzblwͨTȞŖN,"ҴYiމ4}M'`g"R_w?H3TWuwE)/"dRqaߠ>$Dp@OUq809T{nl˭:L[Ix'R]v3~{x'""N$5Cg} Ė8XHt޷e6TDDa/bW[)qpw2Y%` 09xY}H`(0{b<~9ZB:5gbygw/Oc]+m_tF>4 w^t=IؓwvFn`ykFrԒܳїXmX1wuoe6kg v0:+R[ HiWċ7Kc[U8l%X[L'UP_GÁ#BϡqnD*]gn4)^?EmoJv?PSo:vBXKl=PS*:Kn,gsReAMŧ)ӝ}~78gF;]hJ1 8M iF26R%18D|{:N%ni,{V,p 42KaKF!٨,su#>6Ŷe5ٿRQ'?(sCZ lIEY tevL .Cx2wTƲsۮhdwquNj&=4 U7e`<*2,|)[{%;_wJ^!םRa y2D0%_wJ!.J)y29R @6y;RJl }qHIG p#3ۑRz/?<㟹{Qqg6z!ܽ(7`G sfmVǟWƦJym;/0`:+ yŵkSjPMtJ =<T .\zĭh+Zbʬ->0.Ix,m!o5[{|zĴ'Xa/䗁 W=q#Tz6U=Ns.黳L=8e8~`dj܁x-: b}cG3l}-W"\1%<-:~0 ؏vG!dRm&҈M=~]Ov/G<~4\g/ޕPucaJPMW.'[ x;|%pvGY8#:Zax3Da5fOtB0 Sc<)I/g?EXcpS~)9[@:_{(p7wf\̐pcc{\M^9&[uw#svWY֪Ybh"!Ɩ?}lڎ|/CN{+]}," ەNK, CߌN! #{8V&-yqF3m 87NB*I8 .?['_ 3_ܺ@"xCc7_0#"`[*{w"58*?/kʚ"Ex&y !n!iZYjl3yE[ )*c[a1{;<Ꝅ"TȡؼPmdl /zU+6rI#(֙T_+pw7ط]pGzw#pw".nK<x'ڃejt`;ZN{d" _4Gs]? MqYf?Yڱ=Dt4-7yqWf"ev.q4Tc/UvlHMT1os~>=K=&mMɿE ܝ3 TXr{v$*ʭo;==;l*Lr$$ݚ兡ܷ ~.iN}806M; & mY^o܌IITP]BQK̅ twD:dqy'!Xp]7ºw^)7+ Y^은H x'P1(dq ,~u \坄H WK3!Ž .M"$DYi ca~;YGx' t8gyQ|8OaK> <[0ND$'%y&zaہ;# A9/Qajzջ(ݏ-= | =5bzou;a"%_E fc@l۱+ dxd ,vlDe3_EœaF bū$R&/=qR+$nғHUU}a\DR+7)>Ruz'73ww)Tw臽lw~j)wR*Ruy'Ri>tkF`OTU 6o-Dk6w־ZgOQ%@]o4vHz7N@ 3t+Zܡzgy' R*JŽD *R7U9sWq`Rq@ *LN 1 lw_}]"u0ZN;)Mlw"Yjk{'НdHAJY s(e{TˣJ7Ezw@wR-mlSD wMN;ZIw";T;6N@ U,wI@:s(n;SZZEoz1xM(JH %TgjSItm28v;NB$T@W!4$";ȶN ü*~"wMN@$Ma^mo`$֔Rq8;杀Hd;y'I?3Ih} `Uc>iɢ߳X.EPV;OtHl+v\rqYfo*`<\|8Z~2Sn5dD"ywhg } %dpw"\HI _dmEI,ð'\!-!"`jFIdbW/]X[~0v|,l w"MNA+pp#00=9;r+Kg܍3QB}/Rv{zv!on= vw0F,U[_"b"U0;ߧŽ={`t#^؟d\;Fgx'Qq76b+#x'!#h^{l74b>. ^NDjm,h`݁)X-]?u\Ŭ_$~ {l7`amui&ihjw5T-Xl!د% 9; x}! >Q%MwR;_CDa0; mz'Qz'Г B!eXD6.I[faFo[~-؍k+sc|X8;xw9pŞNFpQ3hoD: 1^(lkd#z.]Υ %Kہ؄X7'4șTf,qTVH)+Vc u{41R&hRt` ıSsrj<{iJCY ƝEo5x*ۗсltx9}tWÖGG8^ tUw8v{ˁ#sKMv00qJ9cW5Bt؉-:V 6xHx-k6h]&@H% yR [?n퐆e<~+HbO%Cb@y_'6;qbN?;}Hǯͨ;OiW/eIrLٜ)e<۱E |8cʕ,ٟ<%](e[xfyGh.:wu+.߅Rf$w/Jj?bvA*gv݋P8֌OEIU8X6aaKh{7ϸM;:kFP~ ޫ;V`}) W#zةJƒmѾ:Or$jsl X1"iۛS,ž•:AXX] _ӡѵq1VNBMĞa7eh !usXlx)[S:v01:Tkl1RƘ jkj꺐RCM 9U\5b.,{\ Ln%'eyf;Xi<,{K5E>^},w=3|%48 yhvŋ5΢v؜|Sk)TcV7O5R-hpXn}_g/yذюzZX\X``:V?ƺ_H+Eǰ'49TdlF?*/~@jbKRn{AUCۥOg?P++ed>}]xgOx'uAu#qx}J6އ.Amw޴ZvNڰ~n<b(6`g`'fvIxRqe cga;y)V:%#e?pw XR ;IDAT4 {aGe5;S޹r`.60~XaSH͋{6;#\;i%Ix@I~ HK'CNtCD7}w"^Tl2p mgA{貌-0oЉHzZCP`fňT*ؚR:큣s⼌ lJKq l뜋z6#ns.T'i"fEbgU.-HobE?W~4;$D$)+8KB!$i`lcI *:#"aMf$}w"QI4ZD$iI47TbsXy'""IZ]]H3| ZD >styw""Wz'Ѭ*bӚVy'""Iׂzb[$8 l=YU,`vމHwyեl LNDDJk+6!#iU澦n+RZ]IC=iAމt3X=@11/h މH>a3h!=9\d`:bw EHe@z8Gp]):_>mi9[e,08 V|WOJ\Ji)wr(F,;y{u6aO9 ܆Z n18؜~؆ 5 ؎́3)/ vip(a`gE@mJlW@[v--Qęa7QT[nžR#7uEAv>K)GԞ;Hxip])⬽"X |pDl*l GJh6<~0bl_H#lJb߈.Xi/6n Yx 8?7NR_`0*]/w!Q'ځs_~Dض];J7Zy^PpY.ÿ(c&/vz@I!S3lo|h.h^|}Rr׮Kg-[UAvK9-HVhBys*ͷulHCb3 ٰakX{Ex8rېx[L)-"I}տu=h&MUk߀=Y8 ۽@Z>RĉŤ- {6)Ih9 wǐI)JYjΠvDwARTg` yp c^":^cz4#] }NNDr$5;ˁ˽nBTܥ'z' ;.=㝀vwG]zᝀ?5.=;ɭ;3;)h l`abV7C:c<6Sh]!T'c+,J :h%!AIdЊmxkg U׸M#/F]88fO-bĉ1-"=Kw!'ltQU5_n H͌Ǧz%Cﻮ [D"M#.>[/\J[H -Eb9iUfFH9)"v!-"ζ6.6=ť^ Þ5vPxJ8r_X@N({.a 4;>8; GŽ^!´x' Sw@Nk{' H<*t5/I{|n@˽xRHNC&R5; .N@D.z5= NS"RQ/6Ŵm6S< l"R3)CMltDc9埦)"MN^hĿ֌J`jFH >ف_Q/vC#9 xmH6Og=K_ j8h#~zf7b J$Ҟ>*"%08[>lToL>-axVDl|ūt$Mװ_v@+:~Xa>8rn%:XA|&'@bӗ鯿e0{,S`Clcq*m7K] k %@z~t>vQ !]i*" &Zݫ(n"""""""""""""""""""""""""""""""""""""""""""""""""""""(\IENDB`ucrpf1host-0.0.20170617/images/settings.svg000066400000000000000000000130511312100406100201670ustar00rootroot00000000000000 image/svg+xml Sonshine Design Sonshine Penguin ucrpf1host-0.0.20170617/images/ucrpf1host.svg000066400000000000000000000075631312100406100204400ustar00rootroot00000000000000 image/svg+xml ucrpf1host-0.0.20170617/images/unloadfilament.png000066400000000000000000000276041312100406100213270ustar00rootroot00000000000000PNG  IHDRwUAVsBIT|d pHYsttfxtEXtSoftwarewww.inkscape.org< IDATxweUyTzeD:( bBF4߈ &Ecፚ0jԨX%AH&Rf0ca\| u~:kH$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$IhSJ' uԖ#ȟӀ#,⑿/#OVz4Ti.l`?W}V\V ]1ebqm< k=K&5_7y=, :~6|$(bqzx!"0`ٔ/:`eь4)tӀ`3 %T;S ,$|\i6o'|ˑOÂ*gNIb+m/m'L% .|][P7AIR?6Ng}h4|A3Əˁ#qFRC/M73qjXT[ E}뇀 -}X63 m>*%i#a~p&p.W׋@׮WSlx4cks*E%X aHl¢g:d;R+|KԻӴGs{)~ ׼fT7,\љ0t;|tQNRGRnJ S$ y4œK713pyU%L]P>HZ56ҿ+(_74 7֖x,#XfV~Ͳ=aq)_AXSRF (W$_ p e\{r$6I /#̍kpf.\o 5KT] n>9G*rq(MhIsy",'p3vB:z%y "߮p;~yιH: yy.Eqԟ*{e92~F<Ib=IC~^+[KU|gHɽr딼'?\q_ \ lRAJ]0@ R}9eυq]>PIya] פYSP6JIʟ ^3a!aG~|XFE;6Mu SK?TwRA%`{z8PeG/5T/ʎ^NϿ4I#=¦ѥ/^0<-//a_dqT5͠IB}*C9D(/ۯ).]Nّ6nTU?]SiS:OZRFSS2MZ볔|E~L_<9bT LkWwfIpNxG6jPsi6Z5c4F9kxs*_(9cE*Qs S4TER"ޔ~Z\qT54xH^SV%_PJgp /RX?p?ϒ84DE8&e5vd[FV:78 N^t5åP]LNb>5m5ܙK KEAEqihfU8:6RS|>E0~uF4.,4h<=a}_C=vm26[)5>Ѯ@>yhDvˀn!j9𧥓K֪/-@M>zwLf ܩ.amm- {vn0]L!*p +dMRI_Q'ڜiOIÄ D7;k/qDX'xCq/S y':kpLMqZx'voj&9]:x_WhaѴx0-ݔ?W Ұ 4}_f<#k{6`m-=:C?ˁhO O~KjsS>XPWK'ѾJX:Tq3=65~Sa*asIv&v!*. ̷5ȯ4Qnr,6]_Mxht~9@uTAʟFE)u4`=zgS4B\@X5 \C63֌/M[>@qg> 3S܇+jĕ?!ߥN׳t[!'Tocz2op2r*X;_NDt*2iqFMqO{Sgm(Z\:;,wH:VB__:}040t"5ȭ -Ͷӽ e Nb{2=*51xeqﰨ_}[DXK'R }aN#,A}It"A'S}gߟF\KϤKqt_87V̈́G,^/H5 -5􇮾,%kہwNC,&)Z+z/Tlqo.{yfƷ8 X^: `Zܛ^'3{L5x0/7.h__+DC0/7愥އ ϩZ; "{3 a{Na>KTm0/7ӎhvW-Ds{-ña^:=̋\QfN.&L~Hh-#N7|,Efھt-tflpK5|5G(Y]w&Lli6݃LNe <(;M#1Ϊ?{y{"Ͱw=2!,(6Ӕ#$𛑘Ƅ_;z[9Y׼Ž,i]V:X\AwuXULxLYY"؉z l: ,36ǥhK+cerXLy}-`[LQGw&ԣ'KK'G~+y5Xa6o@˞{ !kRw.`ŋcv @\rYJ8# |SS:t-(P{qn1uoqoL[K'2-cqOkit푯8I[7=J'2gqw޵hoka֓b5/@MȲJ'2 K'2_l7O(j1#E"zJa[Ћ}h<BVF{Ѧ~c>:=O6O\Bt"ܳsW3l1iM,HE{Ұ̵WVϝ[Ž.b$YOa{󴽸闁|b`!vCN%=ގ2ƯK].d s 3 c!_ŶdК,vn{>X'7gz{]"~4 7ZcWsŽy~? |W?⵻%AaE2Z9_YHYt"XN91b^D1ϊ|OmfŽ#!lUw7\Τ |aXS8,m][*q2>Luy p aߨ~g#_X$dqFsN`mNC=%t6}u;=,q_F|P*ǃ_x?b{`?duKU ޣbg; ,tW=8Fz㵑-e*͎|cn 7ݙ DWa_r8$=~"yŽsn>@Zy(ye8Ss'f)5 Uqa HdyN$M;ܤ}Zun{/d/V%a؆RH?W}+14ķ㭩R{Ez,`׼@N.[F`c_ D6&< n#Μ7 3n)HNWq?Οrm1zMeo{4Ћ_@["M{7q1i+WyyEkE/bR4^ tm}Xto!{N)_|?i֙NpƑ0q'^y_QW_ 3פk2箖8ޛso'6/UƷ*]Λb` q'`'ȻAθtm̹e \J7NiwRs)_S-%j-[M_meϺ^\@Px)E#];~>ojw">A{l>Mœ*vH~O;M_m޵SAO.2e#naTmg]IOIézKI=O{*{J.E }!WPB/t2BvFKڶ*q'ٳn.9Gog?xtkWe$y;."կ/R VFmӲ:ğ=ymmGM:gsޜ1'`܉7t{GiJ'MޞY"d0@Ow lxxulCi=$2n֨fgPm}ϐjo#m2qL~HtWqJ^BҜЎNDe8ϏE(q,.i4,|R,!x+)T5اIw1|i&p-]caAt:'KHwQӜʩ/r]oW˓oRmNX)q0# 77:|NS- 5nr@.` .֥ڹ1޲fe"a]j`0|13 y{6͞ M^07 RfyWk#)Ptj A0q )%oz Q!Sϓb:y݋ݑ:hp/k([ڙ4͏=:l]_\ȻKT h^̣{JŭM^ŅMlފ7AS<{^HU#} /f%!5Ml4'%<-iSSEx?p\|?QaF3NْA6’Q.nE@N ,ZՅ8Xo6"d}fo'sYyoL ?ϝX |ILJ kūn&KѶ&<<^/y5ѷM3Gݔ!Ur'rVϠQowΥmIDAT |.wBuL~\jSuq_eO[{͋+gdo] NO+߉yDo5.lIXȟo/GbIM%<IB96y#,‚RÈyg]'j`'s{;섹@NpdNF?||NהIQTWe>N%,!6?(i|BۜpSƒR cS!”#q7̫^l_"{qmு˘!6cu |p'{J$ %?e ?0Fg3]M\ۜV0'-xO SK6G,~JХq^RJ}UtTCFyGpgfI-WW?~ 5}Ex,V2I=__{{{ lvu(p#pa#hɤC .b#$8>>\(fnmMGŬ?k0w$~Ot'aQF?K5f=8.{l8+{]wdfO'o*z6ʂ\ATguAD|0sMq!qOfs^j!sS`D%ICް&|h U^gH'~6Kup*gRBWԷع&{Rf].7z2 %*+=U`]ʬŽ!UY.e>7>$e~կ3缯b]ʬ=W}cSww).2=(bqFXX.e׵>8:|-yY׊&euc]ʬk=*o(s.e֥aJ|ca]fGJ6:¶hw'G#a}z ˏrksTfmv5a|0O'L]/OmOYRl,E19aw Gbb3,5[W܏ec0E,OڱC 1.^N(l 9,#e֕ȟˁaVڱOW cF]]$i\]Avlq_/.ma]RȚ'M n}vʰLA%,a); UoPb,sg]B՛=xw)3{oP8g]B՛2,RfKj Uos2 UIcqaxܥ,T9,.efq͞ƱPf==w)3{oP8g]B՛2,Rf8g]B՛2,Rf8ms2Pf]RX{dz.efqaxw)3 Uo%5Ž7{KYz.q,Ys2L<7{ޛ=xܥ,Tsgq2fq8g]B՛=wIcq͞{<{RfdzKY3X%՞Ž?m68{Rfs(j Ξ?Xܥ,sK={Ǟ?ıKY㰌Fa8ܥ,T.Q,Ǟ?Pcq2aIbq2q(ef=81m搌4{Kj UǞŽ?˔cq0t LcC. {q~R:kǞFPe$5Ž?Hj U($՞?%5Ž?P(xCURX㰌FaIbq2B{U!}ꑿS2I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I)6GwNN@]Z.I-dqKR Y%,BwIj!$]Z.I-dqKR Y%,BwIj!$]Z.I-dqKR Y%,BwIj!$]Z.I-dqKR Y%,BwIj!$]Z.I-dqKR Y%,BwIj!$]Z.I-dqKR Y%,BwIj!$]Z.I-dqKR Y%,BwIj!$]Z.I-dqKR Y%,BwIj!$]Z.I-dqKR Y%,BwIj)=[$L+H"\]:nIH] lIf$m!Dnn*D"ˀKK'"Iu2qcc!Oⶑƚ KwndVJ6bӥYPK'Ts%5:ڑ. 4=$5.S` YU4$š4k}9an$)_ $I=0]pR$fs)_{)mYOyދp- !Iš)#$izX#$MSyӗ$g/-I̹K&q }܉K&RƒG1 `܉K&6,oLq${֒Kʟ$CQzq_$e#?8p`d%Iﷸ?$5?x4%Ib`B9JZ[R J7KǕJP40b`bI o8\j_aKa7ŭ$.' ì*KH\}{ь$IѦT6IR*o!'E:شp.R+8#Au0}!¹Hxq$$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$IRmNlP IENDB`ucrpf1host-0.0.20170617/images/unloadfilament.svg000066400000000000000000000115451312100406100213370ustar00rootroot00000000000000 image/svg+xml ucrpf1host-0.0.20170617/images/utilities.png000066400000000000000000000236161312100406100203370ustar00rootroot00000000000000PNG  IHDRRe`sBIT|d pHYsttfxtEXtSoftwarewww.inkscape.org<XtEXtCopyrightCC0 Public Domain Dedication http://creativecommons.org/publicdomain/zero/1.0/ IDATxyUowVHY0vACXEdDDtdS@3!(2(.((ʎ a $l I:Ǜ4^:SUqΩT9 t,Ż!""e39б,?mH .e ڙx6JD,F ڙ5MD$}k ڙoy5PD$e7|)N{y7B$es>v- ID;,N vnHR pqqX綈kN"}0\ .}~osD % :}T8\OA3KC ;)ZlHV" ڙ6I̧~cD (Ѯ遅m&9ܱ]"Qm Hqhgp|RkHS ڙEяRE72>^ht*ك|0ի"ݳD^XD=b)ʻ-(tΞ0-ޡK摭`SDJcpfXA>X٧"̒׀q Jե"l$iYRN'xϥ"L`l&m—D <+җi^Y*uHt;\/Z"I0D^¶9@"6Jt/ƜZ^&,D5|/{j/ƜZ^6 U,Io`y[Jw /LÁA*'1CpJ?V*R#?yQ`UX"\_KMlM ̼FZJS6_CރX*y!iY 0#w` Wj^ l~>#J:+ n,vS *C 4}! ZT2L%kǿ* F?01c/*fΛ} <) ʜh0v q`hq;-ퟦͱO{7vFoxz)^{+]=?U)7' vSOyبR8[!{bnvQbBUq1r5Z,h(o+$MOd%ٺEK1}^[_?!sݲuJq4S x0uh!hO/PL?C{Lr-JnH^A$&b[ex0UO;p*+ 9,2xAyp>n?~mFe<}t\Amw%c."7St!ş ZBZ`2Nqp@lg!Ɍ}">~Ϲ{%7 Ay&c9?ަf55$1^  zwC_o9Ccma]luOjUYh2,~|`OlmY͂rD 6KvC1)=wE`w(ʹX'kHr?QJӦcsO*bdٽG) Y֗?b;J"bT9s]v$/nlFBT9ZzRjn݀ex76fiwI傓vUz3 Pռҭ?:4(}h˘&x7DJ@{B>M Z\@?ߺqW@1ǎ6w]{x, ſ72T{sLMjfR%;uϭ1a cxwnUs+JiL잠_u lݐ:% A+ζfcXmCEewzw`wl2W*f6?T9S^0VOvL/e݁m7H2RFۅEuHG[p1 n@A3:Qhf?G{95`p9Vż 쒽+~y[=:|܂ި~lz7DwЊ-TxGB .a~ʹT6u>/@: Ļ!*p-R)?l@ =KA쁎WƞHaoxwNղظ~0o~:6qwC*h%pFH)݀ [T!Gg?gX(%wD][@$ ?gX(wBy%䘦U{k`]\ I^YFj6m:nnHM~MYRM)@:{[i}QHQ'6kv|{] wFHjL{b}SZ9GuRc̚9qJ/wc:tf#Y5N| ¿J^#wzlA|te+cGŬ nDiRHGn4l=*s Py7D#6-Ż!JͱV~qU؊nmF2~"mi*x?Sg!Z뱮f5Tӱ} \l}(u&{~o~ԹwRSzMluC__vxWm`ꢌG;n<ґM )DwJ~ ]vAI=}w#$ !t"W`MIөIMn@c)|Oν%Ycirb{"~(Z@al1 k(4}N np]>6<$@[ST7K `40[[iOtFH-ئe6={mclx~"mtW)=Sj~.ǁt=lѲ/;%\2@ڤmF~`l57o(uZ Ta mx\;Hv^=H5G \K6N1TĢF @z(cCyFϋE)Vش(M+)y5<6k:Ў"u~cvڻ1~]qK|'taz>0꘧bm2@|\ o4z| nwv?+=mTX r 4-*uFc%O4sNw6njT=N#?W)/oQl㈑Y9j#\*GM&Khb^&5V&{OI˜IT7?7B倰ɮs'9{_6&{W\N5TŮ!K4s/k#'plrJ:I>?|–9 /FY2خJgsz+.Z%B$ke zvjY nKXcd1Ry:I)K2oQܶkc9F^{O?BOYƿ8-"M>yxWRIF؋ 2P@ñM J>z613!J%~P`s/w{e-=*Ͳcb>+V(5]웷6G|+\ز:,RLK/XY xTy~;pHRI/1r\"Ŷս [)5U9X'g1ѐE*vTwJNgڀO*Xg_X lTp58Fb/F W(_XyZ6VXB܅CX4jkgޠ>ǿ!|.lyەNW;,'1 So_JRڹe2`AxW_Xi'e*Z{Ti46һ1 Q2V`+IsſcJ݁/n4}kC>Vzyʮ"-^W?]c!kd7~Eؚ ./ S~񸽕__8݀L-Mz8-)]9'XmKaw\`eZ ށ ^kS-cVu]?NY^ bM~hP'/%R18V1RAӷ?c-'vR׈عcQ p5'|nerkcO&v`e,uD;{%Tq+ߡ Vfw'4N[Puؽ E$. 3[{2t@hwNէePȌTJ pŽ [XVTaV٘ulTJ\JހHlF fڀ}Bd~֪$ Zxg?Yul:ٞ5 SNoVhlIF'wOaA ëR;l3-O1'1Ν+U1k]df)[=̧ގ6"v.s_" ̦bkZ%a~pJ%h:-:\ͬ9صw}CقzL>*T2mbwF y/6T9 pPKD[TƆA4HbUGx" B'jee8k鑍O$T~6eozDJiN'w}oM=~$'kwC"RywC0 qrH)e~;`Y|ye6!57zt|)y9j֛8ƔR}DVp>]Q@5n2z5KrUTdbz_|Ed)_5åk+OYr"hL]̓v` D$V&/^%g- iv$_hE p /qD{ER0{wCcyJٝ KG*p;wCPoa۹=R)׻׼"> ORנs6&AH*žލVOW$#4[)JWOm Nj^>M)kDAHŞл!k~ش')ESX{Reð" L(IR WIWn4wCMRub_|N'AIk" yZ.Q$dzIq5=@(zCI쫢@ѽu`tTY  ^} -J"R b^r;ͻ51 xCY}\Ls% M߻Y 6YyR]٨uHO6L'ZV?? sK"] cˁur=tjc ?jJlot%M֓g>ͬM{`[9"CSU9~uާ~An"C?idSMPG҆byO= n{eOM>M_/ M>vl(;0s>o/I=۱T`_l"Wgcw!!(GPk > ~g?m|ۿ' ?=3}wT4vb,5/;J <؇5.mMWTخX .K0XځiR-ekhK:M}tYsrv DY1aoI5oSX[^A]K)I˕pލp26VxHH_F&^|{^v}"]lӊy7ÀDZǣl^vg}Ի!L77q1݀{܍էO˕qùa׽b7qn_[' b2Z9ά}"]n54i0pw#rs8=,U xͦ $MP-wځ]$L&ӧH= Z7fZռ !r;ip9@됇ϤffJJ[l17b;HMm,}`D"5a{zk\֩ӰIǯ.S-Sc-Vځ݃Uo3#R,>T֏?bI`Pl1%pJ~MUZ¿6N_`PsBRQ)8Y81)˽a)?N;uzl}Y)-7O$`ͶBjKJurRJ{4'P WMnR~ٽY x'-˿f -)O6e2R|/;x7$am9VR7BU"")0ǻ!yUqBDEnlR@ڂmٻwCD$bkz,ڽ'"e)Ҭ* 'RͭE`GE.4*ۻx-¶F/RߗyƔ,f`> >*"+m8ɻ!*'ҭ_΅Ob%ݽ, UA1M)Yd dsbە4jU*+J WkJyvH`(JrkNdN;_Q;O'plbNBr{a*1ǚ*p^ǧ(/ޗ\͞h EEҀ݀Ny\\1)J2ڤEr;ݛUq_Rl ;Ee  ,ĿzY_g=b&ړBn˗.>ƿsz,lhoۢG -\Cmji;,vME+phxy{y0=MTng Z/5ya~)An#_#%L+ӿU9OYNjO]A\^^5NcvAdw`RXkCOwe(Wl)"p"FqsԱ6ÿ؍{Q*l?)q`:ao:O]VKڛEn$RӀ񯥲<3 ǗBnވo/lYmUS?fU[4uTNYoyo 97g-1x6—# y׸yȯpeq9knʻ* |XeاOχgЮ:f)᜵t%6%#& :y_hoC-nB+g6$ъITg164LlkғynY=Ja-)lfBbKޢ SlvE;-j6 OӲ`9`IDAT;UY=Q=NzJ98u R]cNZx^}ku`4z6||5-U#mQN¿Ou ~Zi5+gߚڲ;p#()7Q1`&a/L?yG[P`xv"x{:FqػGhKQŖ.bԵ6aj/˨esJ=F1*6H>=mv$s?ލHNh%yDϛ.HK%W|QeB`ЅI&3wa{n]1aBF$AJ ~ӊ-}y nTkZޜ92ACFNZL)WlPmB"u \5Jcԣ Cu)Qg#"Wۑj[;JD}}'A &z\\6"UIϔw*DځCFb:M)hůu((D Q  ܉R˔}y)v"4Yin{4m"TnSdwf10bޚNANM)s- 4%"dGqYpq횫ѓyَ,ٙǁ5 ڰ{Y."I,Hmtn\`F-"ɹA0o^6n:6N`f-")dy pC " +fUރ_x/`I>؆qރc ub6A`RH2K$nl[}Vl$,vtKƷ[j$k40 ,wf5Uf5vCE$i)N_ -ό |8)"ٙ6l۔ YQieS#P y1o'"ri6bKJ=}m~""mD.DD /OJ!G&"RS)nFA$"RV&'u@""o=C3{( z4SDjX6D.$ zH턘%0膋dC0&C4>YflYdDo4SD$]Dg#("> rkHIL` ΋H_#8E s[DDJipwCznRHFIENDB`ucrpf1host-0.0.20170617/images/utilities.svg000066400000000000000000000061331312100406100203450ustar00rootroot00000000000000 image/svg+xml Openclipart ucrpf1host-0.0.20170617/properties/000077500000000000000000000000001312100406100165355ustar00rootroot00000000000000ucrpf1host-0.0.20170617/properties/ucrpf1host.properties000066400000000000000000000010601312100406100227460ustar00rootroot00000000000000Back_to_Main=Back to Main Connect=Connect Estimate_Time_COLON=Estimate Time: Extruder_Height=Extruder Height Extruder_Position=Extruder Position Filament=Filament GCode=G-Code Home=Home Info=Info Lines_COLON=Lines: Load_Filament=Load Filament Preheat=Preheat Preheat_Temperature=Preheat Temperature Print=Print Progress_COLON=Progress: Progress=Progress Send=Send Settings=Settings Status_COLON=Status: Status=Status Stop_Loading=Stop Loading/Unloading Temperature_COLON=Temperature: Temperature=Temperature Unload_Filament=Unload Filament Utilities=Utilities ucrpf1host-0.0.20170617/properties/ucrpf1host_zh_TW.properties.txt000066400000000000000000000007721312100406100247100ustar00rootroot00000000000000Back_to_Main=退回主選單 Connect=連接 Estimate_Time_COLON=預測結束時間: Extruder_Height=噴頭高度 Extruder_Position=噴頭位置 Filament=塑料 GCode=G-Code Home=歸位 Info=資訊 Lines_COLON=行數: Load_Filament=進料 Preheat=預熱 Preheat_Temperature=預設溫度 Print=列印 Progress_COLON=進度: Progress=進度 Send=送出 Settings=設定 Status_COLON=狀態: Status=狀態 Stop_Loading=停止 Temperature_COLON=溫度: Temperature=溫度 Unload_Filament=退料 Utilities=工具