From 49440898a880a9db7342f0469794ea7e25770903 Mon Sep 17 00:00:00 2001 From: Nate Jensen Date: Wed, 4 Feb 2015 12:06:46 -0600 Subject: [PATCH] Omaha #4086 resurrect undead textws scripting code and make it work with TextDBQuery Change-Id: I33ddf04b2f02ee088f7b8956c5c31e874747ddf8 Former-commit-id: fa547ac3ddb7e87924f44ac0564eb19e66aa45e6 [formerly 9cdf82d433bd07289002c1f4df9f5242dce0de7e] Former-commit-id: b6fe8b843e3929df3f113c903bb284f9dc078527 --- .../dialogs/util/TextDBUtilities.java | 121 +++++ .../scripting/runner/TextWsCommands.java | 504 ++++++++++++++++++ 2 files changed, 625 insertions(+) create mode 100644 cave/com.raytheon.viz.texteditor/src/com/raytheon/viz/texteditor/scripting/dialogs/util/TextDBUtilities.java create mode 100644 cave/com.raytheon.viz.texteditor/src/com/raytheon/viz/texteditor/scripting/runner/TextWsCommands.java diff --git a/cave/com.raytheon.viz.texteditor/src/com/raytheon/viz/texteditor/scripting/dialogs/util/TextDBUtilities.java b/cave/com.raytheon.viz.texteditor/src/com/raytheon/viz/texteditor/scripting/dialogs/util/TextDBUtilities.java new file mode 100644 index 0000000000..b51048bdd2 --- /dev/null +++ b/cave/com.raytheon.viz.texteditor/src/com/raytheon/viz/texteditor/scripting/dialogs/util/TextDBUtilities.java @@ -0,0 +1,121 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.viz.texteditor.scripting.dialogs.util; + +import java.util.ArrayList; +import java.util.List; + +import com.raytheon.uf.common.message.Message; +import com.raytheon.uf.common.message.Property; +import com.raytheon.viz.texteditor.TextDBQuery; + +/** + * Contains utility methods for interactions with the text database. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Jul 22, 2009            mfegan     Initial creation
+ * Jul 13, 2010 2187       cjeanbap   Add opertional mode functionality.
+ * 02Aug2010    2187       cjeanbap    Update variable/method signature to be consistent.
+ * Feb 04, 2015 4086       njensen     Resurrected class and changed to use TextDBQuery
+ * 
+ * 
+ * + * @author mfegan + * @version 1.0 + */ + +public final class TextDBUtilities { + public static final String TYPE_PROD = "PROD"; + + public static final String TYPE_INFO = "INFO"; + + /** + * + */ + private TextDBUtilities() { + // no class instances + } + + public static String writeProductToDatabase(String prodID, String contents, + boolean operationalMode) throws Exception { + TextDBQuery query = createProductStoreMessage(prodID, contents, + operationalMode); + Message message = query.executeQuery(); + StringBuffer sb = new StringBuffer(); + for (Property property : message.getHeader().getProperties()) { + String value = property.getValue(); + if (value.matches("^NORMAL")) { + sb.append(value.split(":")[1]).append("\n"); + } else { + throw new Exception("Received error from product retrieval - " + + value); + } + } + return sb.toString().trim(); + } + + public static String[] readProductFromDatabase(String prodID, String type, + boolean operationalMode) throws Exception { + TextDBQuery query = createProductRequestMessage(prodID, type, + operationalMode); + Message message = query.executeQuery(); + List products = new ArrayList(); + Property[] properties = message.getHeader().getProperties(); + if (properties == null) { + return null; + } + for (Property property : message.getHeader().getProperties()) { + if ("stdout".equalsIgnoreCase(property.getName())) { + products.add(property.getValue()); + } else { + throw new Exception(property.getValue()); + } + } + + return products.toArray(new String[] {}); + } + + private static TextDBQuery createProductRequestMessage(String prodID, + String type, Boolean operationalMode) { + TextDBQuery query = new TextDBQuery(); + query.setQueryViewName("text"); + query.setQueryOpName("GET"); + query.setQuerySubObName(type); + query.setQueryAfosCmd(prodID); + query.setQueryOperationalMode(operationalMode.toString()); + return query; + } + + private static TextDBQuery createProductStoreMessage(String prodID, + String product, Boolean operationalMode) { + TextDBQuery query = new TextDBQuery(); + query.setQueryViewName("text"); + query.setQueryOpName("PUT"); + query.setQueryProduct(product); + query.addProductId(prodID); + query.setQueryOperationalMode(operationalMode.toString()); + return query; + } + +} diff --git a/cave/com.raytheon.viz.texteditor/src/com/raytheon/viz/texteditor/scripting/runner/TextWsCommands.java b/cave/com.raytheon.viz.texteditor/src/com/raytheon/viz/texteditor/scripting/runner/TextWsCommands.java new file mode 100644 index 0000000000..24f4a85429 --- /dev/null +++ b/cave/com.raytheon.viz.texteditor/src/com/raytheon/viz/texteditor/scripting/runner/TextWsCommands.java @@ -0,0 +1,504 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.viz.texteditor.scripting.runner; + +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import jep.JepException; + +import com.raytheon.uf.common.time.SimulatedTime; +import com.raytheon.viz.core.mode.CAVEMode; +import com.raytheon.viz.texteditor.msgs.IScriptRunnerObserver; +import com.raytheon.viz.texteditor.scripting.dialogs.util.FileUtilities; +import com.raytheon.viz.texteditor.scripting.dialogs.util.TextDBUtilities; +import com.raytheon.viz.texteditor.scripting.dialogs.util.Utilities; + +/** + * Class providing the top level implementations of the special Text WS + * scripting commands. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Jun 29, 2009            mfegan      Initial creation
+ * Jul 13, 2010 2187       cjeanbap    Add operational mode functionality
+ * Feb 04, 2015 4086       njensen     Resurrected class
+ * 
+ * 
+ * + * @author mfegan + * @version 1.0 + */ + +public class TextWsCommands { + private final String TIME_FMT = "%1$tD %1$tT"; + + private String editor = ""; + + private IScriptRunnerObserver observer = null; + + private boolean canceled = false; + + private boolean operationalMode = true; + + /** + * + */ + public TextWsCommands() { + CAVEMode mode = CAVEMode.getMode(); + this.operationalMode = (CAVEMode.OPERATIONAL.equals(mode) + || CAVEMode.PRACTICE.equals(mode) ? true : false); + } + + public void setEditor(String editor) { + this.editor = editor; + } + + public void setObserver(Object observer) { + this.observer = (IScriptRunnerObserver) observer; + } + + /** + * Implements the Text Workstation script runner's {@code run(file)} + * command. The file to execute must be in the current user's home + * directory. + * + * @param file + * name of the script to execute + * + * @throws Exception + * if any problem occurs + */ + public void runLocalFile(String file) throws JepException { + String homeDir = System.getProperty("user.home"); + if (Utilities.isEmptyString(file)) { + throw new JepException("no file specified -- unable to execute"); + } + if (file.indexOf("/") != -1) { + throw new JepException("expected local file but got \"" + file + + "\""); + } + + String script = homeDir + "/" + file; + // try { + // script = FileUtilities.loadFileToString(homeDir, file); + // } catch (IOException e) { + // throw new Exception("could not open \"" + file + "\"",e); + // } + System.out.println(script); + observer.executeTextScript(script); + } + + /** + * Implements the Text Workstation script runner's {@code load(pid)} + * command. Reads the latest product matching the specified PID from the + * text database and sends it to the observer for display. + * + * @param pil + * the product ID (PID) + * + * @throws Exception + * when any error occurs + */ + public void loadTextProduct(String pil) throws Exception { + if (Utilities.isEmptyString(pil)) { + throw new Exception( + "no product ID provided -- unable to load product"); + } + if (observer.isEditMode()) { + throw new Exception("Cannot load product: text window in edit mode"); + } + if (pil.startsWith("E:") || pil.startsWith("M:")) { + throw new Exception( + "Cannot load product: cannot edit products while script is running"); + } + observer.writeText("--- requesting " + pil + + " from text database ---\n"); + String[] products;// = observer.getProductFromDatabase(pid); + try { + products = TextDBUtilities.readProductFromDatabase(pil, + TextDBUtilities.TYPE_PROD, this.operationalMode); + } catch (Exception e) { + observer.writeText("--- product \"" + pil + + "\" not available ---\n"); + observer.showErrorMessage("failure reading from database.", e); + return; + } + if (products == null || products.length == 0) { + observer.writeText("--- product \"" + pil + + "\" not available ---\n"); + observer.showScriptStatus("Requested product \"" + pil + + "\" not found in data base"); + return; + } + observer.postProductToEditor(products, new String[] { pil }); + } + + /** + * Implements the Text Workstation script runner's + * {@code readdb(pid,filename)} command. Reads the latest product matching + * the pid and writes the product to the specified file. Emulates the AWIPS + * I textdb -rd PIL retrieval. + * + * @param pil + * the AFOS PIL to retrieve + * @param filename + * path to the file to contain the results + * + * @throws Exception + * if an error occurs + */ + public void saveProductToFile(String pil, String filename) throws Exception { + if (Utilities.isEmptyString(pil)) { + throw new Exception( + "no product ID provided -- unable to read product"); + } + if (Utilities.isEmptyString(filename)) { + throw new Exception( + "no file name provided -- unable to read product"); + } + observer.writeText("--- requesting " + pil + + " from text database ---\n"); + String[] products = null; + try { + products = TextDBUtilities.readProductFromDatabase(pil, + TextDBUtilities.TYPE_INFO, this.operationalMode); + } catch (Exception e) { + observer.writeText("--- product \"" + pil + + "\" not available ---\n"); + observer.showErrorMessage("failure reading from database.", e); + return; + } + if (products == null || products.length == 0) { + observer.writeText("--- product \"" + pil + + "\" not available ---\n"); + observer.showScriptStatus("Requested product \"" + pil + + "\" not found in data base"); + return; + } + int count = products.length; + String ln = System.getProperty("line.separator", "\n"); + observer.writeText("--- obtained " + count + " records for " + pil + + " ---\n"); + StringBuffer sb = new StringBuffer(); + for (String product : products) { + sb.append(product).append(ln); + } + observer.writeText("--- writing results for " + pil + " to " + filename + + " ---\n"); + try { + FileUtilities.writeStringToFile(filename, sb.toString()); + } catch (Exception e) { + observer.writeText("--- cannot write to " + filename + " ---"); + observer.showErrorMessage("cannot write to " + filename, e); + } + } + + /** + * Implements the Text Workstation script runner's + * {@code writedb(pid,filename)} command. Reads the contents of the + * specified file and posts the contents to the text database using the + * specified product ID. + * + * @param pil + * the product ID + * @param filename + * the path to the data file + * + * @throws Exception + * if any problem occurs + */ + public void readProductFromFile(String pil, String filename) + throws Exception { + if (Utilities.isEmptyString(pil)) { + throw new Exception( + "no product ID provided -- unable to write product"); + } + if (Utilities.isEmptyString(filename)) { + throw new Exception( + "no file name provided -- unable to write product"); + } + observer.writeText("--- reading product from " + filename + " ---\n"); + String contents = ""; + try { + contents = FileUtilities.loadFileToString(filename); + } catch (Exception e) { + throw new Exception("cannot read from " + filename); + } + try { + String result = TextDBUtilities.writeProductToDatabase(pil, + contents, this.operationalMode); + observer.showScriptStatus(result); + } catch (Exception e) { + observer.showErrorMessage("failure writing to database ", e); + } + } + + /** + * Puts the script runner into a "safe" wait state. This state can be + * interrupted by the user in one of two ways; 'Continue' and 'Cancel'. + * + * @throws Exception + * if an error occurs + */ + public void waitIndefinate() throws Exception { + observer.showScriptStatus("Waiting for user to continue..."); + observer.activateControls(false, true); + // indefinite sleep loop + while (true) { + if (observer.cancelScript()) { + canceled = true; + break; + } else if (observer.continueScript()) { + break; + } + try { + Thread.sleep(100); + doEvents(); + } catch (InterruptedException e) { + // nothing to do + } + } + observer.activateControls(false, false); + } + + /** + * Waits until the specified number of minutes after the hour. the number of + * minutes must be between 0 and 59 inclusive. If the specified time is less + * than the current minutes after the hour, the delay is scheduled into the + * next hour. + * + * @param time + * time delay after the hour + * + * @throws Exception + * in case of any error + */ + public void waitUntilTime(int minToWait) throws Exception { + if (minToWait < 0 || minToWait > 59) { + throw new Exception( + "Invalid argument: expected integer between 0 and 59 but got \"" + + minToWait + "\""); + } + /* determine when to end wait */ + Date date = (Date) SimulatedTime.getSystemTime().getTime().clone(); + Calendar target = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + target.setTime(date); + int minPastHour = target.get(Calendar.MINUTE); + if (minPastHour > minToWait) { + // past target -- wait into next hour + target.add(Calendar.HOUR_OF_DAY, 1); + } + target.set(Calendar.MINUTE, minToWait); + target.set(Calendar.SECOND, 0); + /* execute the safe sleep */ + safeSleep(target.getTime()); + } + + /** + * Waits for the specified amount of time. The format of the time + * specification is HH:MM:SS; resulting of a delay of up to 23hrs + * 59min 59sec. + * + * @param time + * the amount of time to delay + * + * @throws Exception + * in case of any error + */ + public void waitForTime(String time) throws Exception { + /* parse/validate the argument */ + Pattern p = Pattern.compile("(\\d{2}):(\\d{2}):(\\d{2})"); + Matcher m = p.matcher(time); + if (!m.matches()) { + throw new Exception( + "Invalid argument: expected format HH:MM:SS but got \"" + + time + "\""); + } + int hrs = 0; + int mins = 0; + int secs = 0; + try { + hrs = Integer.parseInt(m.group(1)); + mins = Integer.parseInt(m.group(2)); + secs = Integer.parseInt(m.group(3)); + } catch (NumberFormatException e) { + throw new Exception( + "Invalid argument: expected format HH:MM:SS but got \"" + + time + "\"", e); + } + if (hrs < 0 || hrs > 23 || mins < 0 || mins > 59 || secs < 0 + || secs > 59) { + throw new Exception( + "Invalid argument: expected format HH:MM:SS but got \"" + + time + "\""); + } + /* delay time in seconds */ + int delay = 3600 * hrs + 60 * mins + secs; + /* create a calendar representing the wait end time */ + Date now = (Date) SimulatedTime.getSystemTime().getTime().clone(); + Calendar date = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + date.setTime(now); + date.add(Calendar.SECOND, delay); + /* execute the safe sleep */ + safeSleep(date.getTime()); + } + + /** + * Safely sleeps the specified number of seconds. + * + * @param sleepToTime + * provides the end time of the sleep + * + * @throws Exception + * if an error occurs + */ + private void safeSleep(Date sleepToTime) throws Exception { + /* short circuit -- return if end time already past */ + if (SimulatedTime.getSystemTime().getTime().after(sleepToTime)) { + return; + } + observer.activateControls(true, false); + observer.showScriptStatus("Waiting until " + + String.format(TIME_FMT, sleepToTime) + " to proceed..."); + while (SimulatedTime.getSystemTime().getTime().before(sleepToTime)) { + if (observer.cancelScript()) { + canceled = true; + break; + } else if (observer.skipWait()) { + break; + } + try { + Thread.sleep(100); + doEvents(); + } catch (InterruptedException e) { + // nothing to do + } + } + observer.activateControls(false, false); + } + + /** + * Turns results accumulation on in the Text Editor Window. + * + * @param flag + * true to start accumulation, false to stop accumulation + * + * @throws Exception + * if an error occurs + */ + public void setAccumulation(boolean flag) throws Exception { + if (observer.isEditMode()) { + throw new Exception( + "Cannot set accumulate: text window in edit mode"); + } + observer.writeText("--- turning accumulation " + (flag ? "on" : "off") + + " ---\n"); + observer.setAccumulation(flag); + } + + /** + * Clears the Text Editor Window + * + * @throws Exception + * if an error occurs + */ + public void clearTextDisplay() throws Exception { + if (observer.isEditMode()) { + throw new Exception("Cannot clear: text window in edit mode"); + } + observer.writeText("--- clearing text display window ---\n"); + observer.clearTextDisplay(); + } + + /** + * Sends the specified text to the observer for display. This method is used + * to cause output from Python's print command to be redirected to the + * observer. + * + * @param text + * the text to display + */ + public void writeText(String text) { + observer.writeText(text); + } + + /** + * Sends the specified text to the observer for display. This method is used + * to capture output from {@code stderr} in the a python script and redirect + * it to the observer. + * + * @param errMsg + * the stderr text to display + */ + public void writeError(String errMsg) { + observer.scriptError(); + writeText(errMsg); + observer.addStdErrMsg(errMsg); + } + + /** + * allows the script to request a refresh of the GUI + */ + public void doEvents() { + while (observer.getDisplay().readAndDispatch()) { + } + } + + // /** + // * + // * @return + // */ + // public boolean continueScript() { + // return observer.continueScript(); + // } + // /** + // * + // * @return + // */ + // public boolean skipWait() { + // return observer.skipWait(); + // } + + /** + * Returns {@code true} is the user has canceled the script via a user + * interface element. This method should be called periodically during loops + * and pauses to determine if a user ordered cancel has occurred. + */ + public boolean cancelScript() { + return observer.cancelScript(); + } + + /** + * Returns {@code true} if the script was canceled. This allows the Python + * wrapper to properly relay the script cancellation to the script runner. + * Note: this is not set by all commands. + */ + public boolean isCanceled() { + return canceled; + } +}