/* Schneelocke Saarinen's engine script for devices using the Black Gazza power grid. 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 . */ // you shouldn't have to change this. integer pwrChan = -139849; // channel used for BG grid communication integer queryChan = 60369; // channel used for user-initiated device queries string configNotecardName = "config"; // name of notecard holding configuration data // variables integer poweron = FALSE; // power on? integer debugMode = FALSE; // debug mode enabled? key pwrGenID = NULL_KEY; // id of the power generator we're using string deviceName; // device name, filled in from config notecard float amps; // current to draw, filled in from config notecard // internal variables key notecardGetLineID = NULL_KEY; integer notecardCurLine = 0; //////////////////////////////////////////////////////////////////////////////// // device sub-system: handles the device's internal affairs and communication // //////////////////////////////////////////////////////////////////////////////// // device was powered off; e.g. turn off screen device_power_on() { // nothing } // device was powered on; e.g. turn on screen device_power_off() { // nothing } // this gets called when the device is reset. device_setup() { // set "not initialized" values deviceName = ""; amps = -1; // start reading configuration notecard. // rest of setup is handled by _device_setup2, via dataserver event. notecardGetLineID = llGetNotecardLine(configNotecardName, 0); } // helper routine for device setup that gets called by dataserver event _device_setup2(string data) { string property = ""; string value = ""; integer sepPos = -1; // data is the current line from the configuration notecard if(data != EOF) { data = llStringTrim(data, STRING_TRIM); if(llGetSubString(data, 0, 0) != "#") { // skip comments sepPos = llSubStringIndex(data, "="); if(sepPos != -1) { property = llToLower(llStringTrim(llGetSubString(data, 0, sepPos - 1), STRING_TRIM)); value = llToLower(llStringTrim(llGetSubString(data, sepPos + 1, -1), STRING_TRIM)); if(property == "devicename") { deviceName = value; } else if(property == "amps") { amps = (float)value; } else if(property == "debug") { if(value == "true") { debugMode = TRUE; } else if(value == "false") { debugMode = FALSE; } else { llOwnerSay("Malformed boolean value (ignored): " + property + "=" + value); } } else { llOwnerSay("Unrecognized configuration property (ignored): " + property + "=" + value); } } else if(data != "") { llOwnerSay("Malformed configuration line (ignored): " + data); } } // line 0 has already been read in state_entry event. notecardGetLineID = llGetNotecardLine(configNotecardName, ++notecardCurLine); } else { // EOF hit; entire config notecard has been read if((deviceName != "") && (amps != -1)) { grid_setup(); llListen(queryChan, "", NULL_KEY, ""); } else { llOwnerSay("Could not initialize. Bad configuration data: deviceName=" + deviceName + ", amps=" + (string)amps); } } } // handle device message _device_handle_message(integer num, string str) { debugMsg("Handling device message: num=" + (string)num + ", str=" + str); if(str == "power") { if(num == 1) device_power_on(); else device_power_off(); } else { debugMsg("unknown message type: " + str); } } // handle device query _device_handle_query(string msg, key id, string name) { list queryWords = []; string command = ""; string opt1 = ""; string opt2 = ""; string optall = ""; string result = ""; integer forme = FALSE; integer forall = FALSE; debugMsg("Handling query: " + msg); // split along whitespace queryWords = llParseString2List(llToLower(msg), [" "], []); command = llList2String(queryWords, 0); // options specified? if(llGetListLength(queryWords) > 1) { optall = llDumpList2String(llList2List(queryWords, 1, -1), " "); // options start with devicename if(llGetSubString(optall, 0, llStringLength(deviceName) - 1) == llToLower(deviceName)) { forme = TRUE; // further options specified? if(optall == llToLower(deviceName)) { optall = ""; } else { optall = llStringTrim(llGetSubString(optall, llStringLength(deviceName), -1), STRING_TRIM); } } else if(llGetSubString(optall, 0, 2) == "all") { forall = TRUE; // further options specified? if(optall == "all") { optall = ""; } else { optall = llStringTrim(llGetSubString(optall, 3, -1), STRING_TRIM); } } // at this point, optall contains the remaining options. queryWords = llParseString2List(llToLower(optall), [" "], []); opt1 = llToLower(llList2String(queryWords, 0)); opt2 = llToLower(llList2String(queryWords, 1)); } debugMsg("command=" + command + ", forme=" + (string)forme + ", optall=" + optall + ", opt1=" + opt1 + ", opt2=" + opt2); if(!forme && !forall) { return; } // COMMANDS THAT CAN AFFECT ALL DEVICES GO IN THIS SECTION. if(command == "status") { result += "device=" + deviceName + ", amps=" + (string) amps + ", currently turned "; if(poweron) { result += "ON"; } else { result += "OFF"; } } if(command == "debug") { if(opt1 == "on") { debugMode = TRUE; result += "debug messages (to owner) enabled"; } else if(opt1 == "off") { debugMode = FALSE; result += "debug messages (to owner) disabled"; } else if(opt1 == "") { result += "debug messages (to owner) are "; if(debugMode) { result += "ON"; } else { result += "OFF"; } } } if(command == "power") { if(opt1 == "on") { grid_powerup(); result += "powering up."; } else if(opt1 == "off") { grid_powerdown(); result += "powering down."; } } if(command == "reset") { grid_powerdown(); llResetScript(); // this line is never reached } if(!forme) { if(result != "") { // 2-second delay! llInstantMessage(id, result); } return; } // COMMANDS THAT CAN ONLY AFFECT A SPECIFIC DEVICE GO IN THIS SECTION. if(command == "setamps") { if(poweron) { result += "cannot change current drawn while device is running!"; } else { float newamps = (float)opt1; if(newamps == 0.0) { result += "cannot set current drawn to 0!"; } else { amps = newamps; result += "current drawn changed to " + (string)newamps + "."; } } } if(result != "") { // 2-second delay! llInstantMessage(id, result); } } // internal helper: manage device power. _device_set_power(integer newpower) { poweron = newpower; llMessageLinked(LINK_SET, poweron, "power", NULL_KEY); } ///////////////////////////// // BG power grid routines // ///////////////////////////// // BG grid: send grid message to specified id, or entire region if NULL_KEY _grid_send_message(string msg, key id) { if(id == NULL_KEY) { debugMsg("sending grid message: " + msg); llRegionSay(pwrChan, msg); } else { debugMsg("sending grid message to " + (string)id + ": " + msg); llRegionSayTo(id, pwrChan, msg); } } // BG grid: handle a message from the BG power grid. _grid_handle_message(string msg, key id) { debugMsg("got grid message: " + msg); if(llGetSubString(msg, 0, 2) == "ACK") { if(poweron == TRUE) { // already got power; disconnect secondary generators _grid_send_message("FIN", id); } else { _device_set_power(TRUE); pwrGenID = id; } } if(msg == "OVERLOAD" && id == pwrGenID) { grid_overload(); } if (msg == "PING" && poweron == TRUE && id == pwrGenID) { _grid_send_message("PONG", id); } } // BG grid: power down grid_powerdown() { _grid_send_message("FIN", pwrGenID); _device_set_power(FALSE); } // BG grid: power up grid_powerup() { _grid_send_message("FIN", pwrGenID); _grid_send_message("REQ " + (string) amps, NULL_KEY); } // BG grid: overload grid_overload() { llSay(0, "The " + deviceName + " spontaneously turns off."); _device_set_power(FALSE); } // BG grid: setup grid_setup() { // listen for BG power grid messages llListen(pwrChan, "", NULL_KEY, ""); } // helper: send debug message debugMsg(string msg) { if(debugMode) { llOwnerSay("DEBUG: " + msg); } } default { state_entry() { device_setup(); } touch_start(integer total_number) { debugMsg("Touched."); if(poweron == TRUE) { // powered off: notify BG grid and power off. grid_powerdown(); } else { // powered on: request power from grid. grid_powerup(); } } link_message(integer sender_num, integer num, string str, key id) { // handle messages from linked prims _device_handle_message(num, str); } listen(integer channel, string name, key id, string msg) { if(channel == pwrChan) { // handle BG grid message. _grid_handle_message(msg, id); } else if(channel == queryChan) { // handle device query. _device_handle_query(msg, id, name); } } dataserver(key queryID, string data) { debugMsg("dataserver event, query=" + (string)queryID + ", data=" + data); // device setup if(queryID == notecardGetLineID) { _device_setup2(data); } else { debugMsg("Unknown/unexpected dataserver event: queryID=" + (string)queryID + ", data=" + data); } } }