From 9191608cce33af59eb3406dbba3d45de2dcd2af4 Mon Sep 17 00:00:00 2001 From: jgyates Date: Wed, 1 Nov 2023 20:33:10 -0400 Subject: [PATCH] V1.18.18 --- data/controller/Evolution_Liquid_Cooled.json | 4 +- genmonlib/controller.py | 22 +- genmonlib/custom_controller.py | 14 +- genserv.py | 2 +- static/genmon.js | 307 +++++++++++++------ 5 files changed, 227 insertions(+), 122 deletions(-) diff --git a/data/controller/Evolution_Liquid_Cooled.json b/data/controller/Evolution_Liquid_Cooled.json index d7140863..d68ebe36 100644 --- a/data/controller/Evolution_Liquid_Cooled.json +++ b/data/controller/Evolution_Liquid_Cooled.json @@ -1240,11 +1240,11 @@ "comment": "The 'command_sequence can be one or more commands", "command_sequence" : [ { - "comment": "the value written to 'reg' is supplied by the user. 'input_title' is required. 'length' (number of mobus bytes to write) is 2 by default,but must be a multiple of 2", + "comment": "the value written to 'reg' is supplied by the user. 'input_title' is required. 'length' (number of modbus bytes to write) is 2 by default,but must be a multiple of 2", "comment2": "'bounds_reg' is optional but allows the user interface to bounds check the data before sending it to genmon", "comment3": "'tooltip' is optional but allows the user interface to display more information regarding the input.", "reg": "023e", - "input_title": "Exercise Duration", + "input_title": "Minutes", "type": "int", "length": 2, "bounds_regex":"^([1][2-9])|([2-5][0-9])|([6][0])$", diff --git a/genmonlib/controller.py b/genmonlib/controller.py index 9b08f1ce..79ae23c6 100644 --- a/genmonlib/controller.py +++ b/genmonlib/controller.py @@ -844,6 +844,7 @@ def SetCommandButton(self, CommandString): CommandSetList = json.loads(EntryString) # validate object if not isinstance(CommandSetList, list) and not (len(CommandSetList) == 0): + self.LogError("Invalid button object in SetCommandButton") return "Error: Invalid button object" # Execute Command return self.ExecuteRemoteCommand(CommandSetList) @@ -852,7 +853,7 @@ def SetCommandButton(self, CommandString): return "Error: Invalid input for SetCommandButton (2), see error log." else: self.LogError("Error in SetCommandButton: invalid input: " + str(CommandString)) - return "Error: Invalid input for SetButton (3)." + return "Error: Invalid input for SetCommandButton (3)." return "OK" except Exception as e1: self.LogErrorLine("Error in SetCommandButton: " + str(e1)) @@ -879,7 +880,7 @@ def ExecuteRemoteCommand(self, CommandSetList): if not "onewordcommand" in button_command.keys(): self.LogError("Error on ExecuteRemoteCommand, invalid dict: " + str(button_command)) return "Error: invalid input in ExecuteRemoteCommand (2)" - # make a copy of the dict so we can add the input without modifying the origianl + # make a copy of the dict so we can add the input without modifying the original returndict = self.GetButtons(singlebuttonname = button_command["onewordcommand"]) if returndict == None: self.LogError("Error on ExecuteRemoteCommand, command not found: " + str(button_command)) @@ -890,7 +891,7 @@ def ExecuteRemoteCommand(self, CommandSetList): return "Error: invalid command in ExecuteRemoteCommand (2)" # selected_command from genmon, button_command from UI - if not "command_sequence" in selected_command or not "command_sequence" in button_command: + if not "command_sequence" in selected_command.keys() or not "command_sequence" in button_command.keys(): self.LogError("Error on ExecuteRemoteCommand, command sequence mismatch: " + str(button_command)) return "Error on ExecuteRemoteCommand, command sequence mismatch" if not (len(selected_command["command_sequence"]) == len(button_command["command_sequence"])): @@ -898,13 +899,13 @@ def ExecuteRemoteCommand(self, CommandSetList): return "Error on ExecuteRemoteCommand, command sequence mismatch (2)" # iterate thru both lists of commands for gm_cmd, ui_cmd in zip(selected_command["command_sequence"], button_command["command_sequence"]): - if "input_title" in gm_cmd and "value" in ui_cmd: - if "bounds_regex" in gm_cmd: + if "input_title" in gm_cmd.keys() and "value" in ui_cmd.keys(): + if "bounds_regex" in gm_cmd.keys(): if not re.match(gm_cmd["bounds_regex"], str(ui_cmd["value"])): self.LogError("Error in ExecuteRemoteCommand: Failed bounds check: " + str(ui_cmd)) return "Error in ExecuteRemoteCommand: Failed bounds check" - if "type" in gm_cmd and gm_cmd["type"] == "int": - if not "length" in gm_cmd or ("length" in gm_cmd and gm_cmd["length"] == 2): + if "type" in gm_cmd.keys() and gm_cmd["type"] == "int": + if not "length" in gm_cmd.keys() or ("length" in gm_cmd.keys() and gm_cmd["length"] == 2): gm_cmd["value"] = "%04x" % int(ui_cmd["value"]) elif "length" in gm_cmd and gm_cmd["length"] == 4: gm_cmd["value"] = "%08x" % int(ui_cmd["value"]) @@ -914,8 +915,9 @@ def ExecuteRemoteCommand(self, CommandSetList): else: self.LogError("Error in ExecuteRemoteCommand, unsupported type: " + str(ui_cmd)) return "Error in ExecuteRemoteCommand, unsupported type" - elif not "reg" in gm_cmd or not "value" in gm_cmd: + elif not "reg" in gm_cmd.keys() or not "value" in gm_cmd.keys(): self.LogError("Error in ExecuteRemoteCommand, invalid command in sequence: " + str(selected_command)) + self.LogDebug(str(button_command)) return "Error in ExecuteRemoteCommand, invalid command in sequence" # execute the command selected_command return self.ExecuteCommandSequence(selected_command["command_sequence"]) @@ -972,7 +974,7 @@ def ExecuteCommandSequence(self, command_sequence): self.LogDebug("Error in ExecuteCommandSequence: invalid value type") return "Command not found." - return "Remote command sent successfully" + return "OK" except Exception as e1: self.LogErrorLine("Error in ExecuteCommandSequence: " + str(e1)) self.LogDebug(str(command_sequence)) @@ -988,7 +990,7 @@ def GetButtons(self, singlebuttonname = None): # get full simplified list for GUI return {} except Exception as e1: - self.LogErrorLine("Error in SetButton: " + str(e1)) + self.LogErrorLine("Error in GetButtons: " + str(e1)) return {} # ------------ GeneratorController::GetStartInfo ---------------------------- # return a dictionary with startup info for the gui diff --git a/genmonlib/custom_controller.py b/genmonlib/custom_controller.py index 62a0062e..ddba42e8 100644 --- a/genmonlib/custom_controller.py +++ b/genmonlib/custom_controller.py @@ -1290,15 +1290,8 @@ def GetDisplayList(self, inputdict, key_name, JSONNum=False, no_units=False): return ReturnValue return ReturnValue - # -------------CustomController:SetButton------------------------------------ - def SetButton(self): - try: - pass - except Exception as e1: - self.LogErrorLine("Error in SetButton: " + str(e1)) - return {} - # -------------CustomController:GetButtons----------------------------------- - def GetButtons(self): + # -------------CustomController:GetButtons-------------------------------- + def GetButtons(self, singlebuttonname = None): try: button_list = self.controllerimport.get("buttons", None) @@ -1358,6 +1351,9 @@ def GetButtons(self): if CommandError: continue + if singlebuttonname != None and singlebuttonname == button["onewordcommand"]: + return button + return_buttons.append(button) return return_buttons diff --git a/genserv.py b/genserv.py index 75e3ee9d..1160a9e6 100644 --- a/genserv.py +++ b/genserv.py @@ -526,7 +526,7 @@ def ProcessCommand(command): except Exception as e1: data = "Retry" - LogError("Error on command function: " + str(e1)) + LogErrorLine("Error on command function: " + str(e1)) if command in [ "status_json", diff --git a/static/genmon.js b/static/genmon.js index 9416533e..84f1f62e 100644 --- a/static/genmon.js +++ b/static/genmon.js @@ -790,29 +790,12 @@ function DisplayMaintenance(){ if ((command_sequence.length >= 1) && (command_sequence[0].hasOwnProperty("input_title"))){ // TODO WORK IN PROGRESS below this point continue; - // this button has an input, loop thru the list of commands in command_sequence - for (let cmdidx in command_sequence){ - // cycle through each command in command_sequence - command = command_sequence[cmdidx] - if ((command.hasOwnProperty("input_title")) && (command.hasOwnProperty("type"))) { - title = command["input_title"]; - type = command["type"]; - tooltip = "" - bounds = "" - if (command.hasOwnProperty("bounds_regex")){ - bounds = command["bounds_regex"]; - } - if (command.hasOwnProperty("tooltip")){ - tooltip = command["tooltip"]; - } - var default_value = 0 - outstr += setupInputButton(cmdidx, button_command, type, title, default_value, tooltip, bounds ) - } - } + // this button has an input + outstr += setupCommandButton(button); } else { + // This is just a button, no input from the user. outstr += '  

'; } - } } } catch(err){ @@ -838,6 +821,100 @@ function DisplayMaintenance(){ }}); } +//***************************************************************************** +// called to setup button for a command_sequence +//***************************************************************************** +function setupCommandButton(button){ + try{ + var outstr = ""; + var button_command = button["onewordcommand"]; + var button_title = button["title"]; + var command_sequence = button["command_sequence"]; + var button_id = 'button_' + button_command; + var clickCallback = "onCommandButtonClick(\'" + button_command + "\')"; + + outstr += '  '; + outstr += ''; + // loop thru the list of commands in command_sequence + for (let cmdidx in command_sequence){ + // cycle through each command in command_sequence + command = command_sequence[cmdidx] + if ((command.hasOwnProperty("input_title")) && (command.hasOwnProperty("type"))) { + title = command["input_title"]; + type = command["type"]; + tooltip = "" + bounds = "" + if (command.hasOwnProperty("bounds_regex")){ + bounds = command["bounds_regex"]; + } + if (command.hasOwnProperty("tooltip")){ + tooltip = command["tooltip"]; + } + var default_value = 0; + outstr += setupInputBoxForButton(cmdidx, button_command, type, title, default_value, tooltip, bounds ); + } + else{ + console.log("Error: button command_sequence does not have both 'input'title' and 'type'."); + return ""; + } + } + return outstr; + } + catch(err){ + console.log("Error in setupCommandButton: " + err); + return ""; + } +} +//***************************************************************************** +// called to setup input button +//***************************************************************************** +function setupInputBoxForButton(identifier, parent, type, title, default_value, tooltip, bounds_regex ) { + + var outstr = "" + try { + + if (!(type === "int")){ + // at the moment only "int" is supported + return outstr; + } + var id = parent + "_" + identifier + var input_id = "input_"+ id; + + // TODO WORK IN PROGRESS + if (false) + { + var clickCallback = "validateInputButton(\'click\', \'" + identifier + "\', \'" + parent + "\', \'" + bounds_regex + "\')"; + var changeCallback = "validateInputButton(\'change\', \'" + identifier + "\', \'" + parent + "\', \'" + bounds_regex + "\')"; + var button_id = "button_" + id; + validation = 0; + outstr += '
'; + outstr += '
' + + '  ' + + //'onclick="' + clickCallback + ';" ' + + //'' + + '' + + '  ' + + '' + + '' + + (((typeof tooltip !== 'undefined' ) && (tooltip.trim() != "")) ? '' : "") + + '
'; + outstr += '
'; + } + else{ + + outstr += '  '; + outstr += ''; + } + return outstr; + } + catch(err) { + console.log("Error in setupInputBoxForButton: " + err); + } + + return outstr; +} //***************************************************************************** // given a button one word command, retrieve the containing button object @@ -861,15 +938,15 @@ function getButtonFromCommand(onewordcommand){ //***************************************************************************** // called when sending button input to genmon //***************************************************************************** -function setButtonCommand(button_object) +function sendButtonCommand(button_object) { try{ if ((!(button_object.hasOwnProperty("onewordcommand"))) || (!(button_object.hasOwnProperty("title"))) || (!(button_object.hasOwnProperty("command_sequence")))) { - console.log("Error: invalid formate of button object."); - return; + console.log("Error: invalid of button object."); + return false; } // set button command @@ -877,66 +954,152 @@ function setButtonCommand(button_object) // myGenerator['buttons'] list with the 'value' property added // to the command_sequence, // e.g. myGenerator['buttons'][0]['command_sequence][0]['value'] = user defined input - var input = JSON.stringify(button_object); + var error_occured = false; + var input = JSON.stringify([button_object]); var url = baseurl.concat("set_button_command"); $.getJSON( url, - {setremote: input}, + {set_button_command: input}, function(result){ // result should be either "OK" or error string. if (result !== "OK"){ - + // + console.log("Error: failure sending set_button_command: " + result); + error_occured = true; + return false; } }); - + return (error_occured == false); } catch (err){ - console.log("Error in setButonCommand: " + err) + console.log("Error in setButonCommand: " + err); + return false; } } //***************************************************************************** +// +//***************************************************************************** +function onCommandButtonClick(onewordcommand){ + + try{ + // here we want to loop thru the command_sequence arrary, getting the + // data for each input box it corrosponds to, validate the data with the + // bounds_regex parameter, if it exists. Write the value to the entry in + // the command_sequence and send the entire command_button object to genmon. + + var original_button = getButtonFromCommand(onewordcommand); + let button = { ...original_button }; // clone the button object + var button_title = button["title"]; + var command_sequence = button["command_sequence"]; + + // loop thru the list of commands in command_sequence + for (let cmdidx in command_sequence){ + // cycle through each command in command_sequence + command = command_sequence[cmdidx]; + if ((command.hasOwnProperty("input_title")) && (command.hasOwnProperty("type"))) { + title = command["input_title"]; + type = command["type"]; + + bounds = "" + if (command.hasOwnProperty("bounds_regex")){ + bounds = command["bounds_regex"]; + } + if (command.hasOwnProperty("tooltip")){ + tooltip = command["tooltip"]; + } + var input_id = "input_"+ onewordcommand + "_" + cmdidx; + var value = document.getElementById(input_id).value; + if (!(validateRegEx(value, bounds, dialog_on_error = true))){ + console.log("Error: input failed validation in onCommandButtonClick: " + value + ", " + bounds) + return false; + } + if (type == "int"){ + command['value'] = parseInt(value); + } + else { + console.log("Error: unsupported type in onCommandButtonClick: " + type) + return false + } + + } + else{ + console.log("Error: button command_sequence does not have both 'input'title' and 'type'."); + return false; + } + } + // now send the button to genmon for writing + // for now we only send one button at a time. + sendButtonCommand(button); + } + catch(err){ + console.log("Error in onCommandButton: " + err); + return false; + } +} +//***************************************************************************** // called when validating input button // action is "validate", "click" or "change" +// click - validate and send data +// validate - check data and send mesage to user on invalid data +// change - check the data, return true if data OK, otherwise false // identifier is the index of the command_sequence in a given button object // parent is the 'onewordcommand' of the parent // bounds_regex is the regular expession string to bounds check the input //***************************************************************************** function validateInputButton(action, identifier, parent, bounds_regex){ - console.log("Input Validation called: " + action + "," + identifier + ", " + parent) + console.log("Input Validation called: " + action + ", " + identifier + ", " + parent) try{ + // TODO this only does one input now. need to read (and validate) all inputs + // and fill them into the command_sequence then send them to genmon + // the function parameter bounds_regex should be changed as this will come from the + // button_object.command_sequence array entries var button_object = getButtonFromCommand(parent); var button_title = button_object['title']; // get the input value for the corrosponding button var id = parent + "_" + identifier var input_id = "input_"+ id; var value = document.getElementById(input_id).value - - var bounds = new RegExp(bounds_regex); - if (!(bounds.test(value))){ - console.log("Error: input out of bounds.") - // TODO error message to user? - return; - } switch (action) { case "validate": - msg = ""; - break; - case "click": - msg = ""; - break; case "change": - msg = ""; - break; + return validateRegEx(value, bounds_regex, dialog_on_error = (action === "validate")); + case "click": + if (!(validateInputButton("validate", identifier, parent, bounds_regex))){ + return false; + } + // send data to genmon + return true; default: console.log("Error: Invalid action in validateInputButton!"); + return false; } } catch(err){ console.log("Error in validateInputButton: " + err); - return + return false; } + return false; +} +//***************************************************************************** +// validate a value with a regex string, return true or false +//***************************************************************************** +function validateRegEx(value, bounds_regex, dialog_on_error = true){ + try{ + var bounds = new RegExp(bounds_regex); + if (!(bounds.test(value))){ + if (dialog_on_error){ + GenmonAlert("The input is invalid for this parameter."); + } + return false; + } + return true; + } + catch(err){ + console.log("Error in validateRegEx: " + bounds_regex + ": " + err); + return false; + } } //***************************************************************************** // submit for button commands @@ -950,63 +1113,7 @@ function submitButton(ctlid, identifier, parent){ console.log("Error in submitButton: " + err) } } -//***************************************************************************** -// called to setup input button -//***************************************************************************** -function setupInputButton(identifier, parent, type, title, default_value, tooltip, bounds_regex ) { - var outstr = "" - try { - - if (!(type === "int")){ - // at the moment only "int" is supported - return outstr; - } - - // this form is for the tool top support - var validationCallback = "validateInputButton(\'validate\', \'" + identifier + "\', \'" + parent + "\', \'" + bounds_regex + "\');"; - var clickCallback = "validateInputButton(\'click\', \'" + identifier + "\', \'" + parent + "\', \'" + bounds_regex + "\')"; - var changeCallback = "validateInputButton(\'change\', \'" + identifier + "\', \'" + parent + "\', \'" + bounds_regex + "\')"; - var id = parent + "_" + identifier - var button_id = "button_" + id; - var input_id = "input_"+ id; - validation = 0 - - // TODO WORK IN PROGRESS - if (false) - { - outstr += '
'; - //outstr += ''; - outstr += '
' + - '  ' + - //'onclick="' + clickCallback + ';" ' + - //'' + - '' + - '  ' + - '' + - '' + - (((typeof tooltip !== 'undefined' ) && (tooltip.trim() != "")) ? '' : "") + - '
'; - outstr += '
'; - } - else{ - outstr += '  '; - outstr += ''; - outstr += '  '; - outstr += ''; - } - return outstr; - - - } - catch(err) { - console.log("Error in setupInputButton: " + err); - } - - return outstr -} //***************************************************************************** // called when Monthly is clicked //*****************************************************************************