Advertisement
drpepper240

GPC Server - functions 0.70e

Jul 12th, 2014
329
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 51.68 KB | None | 0 0
  1. --computercraft
  2. --general-purpose controller server by drPepper
  3.  
  4. -- "THE BEER-WARE LICENSE"
  5. -- drPepper@KOPROKUBACH wrote this file. As long as you retain this notice you
  6. -- can do whatever you want with this stuff. If we meet some day, in-game or IRL,
  7. -- and you think this stuff is worth it, you can give me a beer in return
  8.  
  9. --don't forget to update this string
  10. VERSION_STRING = "0.70e"
  11.  
  12. ---------------------------------------------------------------------------------------------------
  13. -------------------         README
  14. ---------------------------------------------------------------------------------------------------
  15.  
  16. -- Not actually a readme, just some hints on what is it and how to use it.
  17.  
  18. -- This is a library (literally) of functions, which may help you to build a computercraft server with GUI and touch screen operation.
  19.  
  20. -- By "server" I mean CC console with a modem and a touchscreen, using which you can control remotely some other CC consoles connected to the same network. For example, some will switch redstone output, some will sense redstone input, some will use other CC peripheral APIs to control various peripherals. These remote consoles are mentioned as "clients" or "controllers/sensors"
  21. -- Why don't I use CC Remote Peripherals for the latter? Because you'll never tell which laser is "laser_003" and which is "laser_123" after someone else connects them all as remote peripherals to your network with modems.
  22.  
  23. -- This is NOT a working code, so you shouldn't expect this to run on your CC, although you can find some code besides function bodies left unintentionally or with illustrative purpose.
  24.  
  25. -- I'm trying to maintain this code bug-free and working, but I'm unable to check it and retest regularly (== at least every month). If you absolutely sure you must contact me, feel free to use (but don't abuse) my skัƒั€ะต drpepper<underscore>mc
  26.  
  27. -- Why don't I make an "API" out of this? Because:
  28. --      you can't define a variable or a constant in the CC "API" and access it both from the API functions and program functions: actually there will be a copy of such variable inaccessible to API functions.
  29. --      you can still pass these vars as function arguments, but sometimes there are too many. Way too many. Fuck it.
  30. --      also there are some program- and application-specific types of temporary data. For example, you want to store something in the CC server messing with your lasers, and you don't want to create such variables/tables on your other CC servers, and this data is temporary.
  31. --      and, if none of the above is applicable, the function is marked as "API compatible". I plan to move such functions to the loadable API someday.
  32.  
  33. -- How does it work? Firstly, there are threads. No, not exactly, but CC Parallel API works and is easy to use and hard to misuse, and true treads don't and aren't.
  34. -- Note: various sleep and timer functions either do not work or aren't robust. My advise is to use "network timers": simple non-parallel program which sends messages on a specific channel.
  35.  
  36. -- Threads. There are at least three of them: first one receives network messages, second one is draws or redraws all GUI or single widgets, and the last processes user input (and, therefore, sending messages). Just like in the IPhone, but better and free of charge.
  37.  
  38. -- "sdata" file: there are three tables there: "guiData", "ctrlData" and "settings". First is used in GUI look and operating, second - in message receiving/processing. Each server must use its own sdata file with its own settings and GUI elements. You should't modify its contents during the execution and should edit it on the pastebin and just update it from CC. Store settings in a different file and use ReadResume/WriteResume for your convenience.
  39.  
  40. -- Touch-screen operation:
  41. --      T_guiXy - There is a big table equal to the monitor resolution (for now only one touch-screen is supported), each [x][y] cell either is empty (or do not exist) or contains an ID named guiDataId. This ID is taken from sdata.guiData[i] and is written to the T_guiXy only after a widget is drawn. This table and ID are used to process touchscreen clicks. This table is temporary.
  42. --      After guiDataId is obtained, it is passed to appropriate "clicking" function which does something: sends, writes to console or draws something on the screen.
  43.  
  44. -- Receiving thread operation:
  45. --      T_ctrlTempData - This is the table where the last received data from a specific controller is stored. Receiving thread may post an event to redraw all screen or just specific controllers affected by the message. Populated during "drawing" from guiData[i].controllerIds[], temporary. ControllerIDs are from sdata.ctrlData[i], widgetIDs to redraw - from sdata.ctrlData[i].guiIds[]
  46.  
  47. -- Redrawing thread operation:
  48. --      waits for a redraw event and calls appropriate "drawing" function
  49.  
  50. -- Modes:
  51. --      some widgets can be drawed differently according to the current mode stored in a variable "guiMode". Default is "MODE_DEFAULT"
  52.  
  53. -- Widget types:
  54. --      are used to determine which widget drawing function to call
  55.  
  56. -- Controller states:
  57. --      are named by color, and corresponding color is used in the widget associated with a controller. DO NOT USE ORANGE (or any other color) representing missing (unresponsive) controllers. E.g. "green" is "on" and "red" is "off". You can set the controller to act vice-versa, the server does not need to know about it.
  58.  
  59. -- Q: "This is so complicated, why don't you use _G or other LUA 1337 features?"
  60. -- A: Because I don't know what will CC developers limit in the next release and what will not work as described. Things I use are supported and maintained by CC devs, therefore simple and robust.
  61.  
  62. -- Q: How to use it goddammit?
  63. -- A: Create "startup" and "sdata" files using functions from this file and sample sdata files, and download them from pastebin to your CC console. It's better to declare all used functions first and paste their bodies below: you can keep them in a preferred order without conflicts.
  64.  
  65. -- "Standards":
  66. --      CamelCase
  67. --      first letter in variables/constants is lowerCase, in functions - UpperCase
  68. --      all tables not from sdata must begin with "T_". Or at least global ones. This is handy, trust me.
  69. --      Wanna hardcode something? Put it into sdata.settings.
  70.  
  71. -- Happy debugging!
  72.  
  73.  
  74.  
  75. ---------------------------------------------------------------------------------------------------
  76. -------------------         LISTS
  77. ---------------------------------------------------------------------------------------------------
  78.  
  79. -------------------         WIDGET TYPES
  80.  
  81. --AIRGEN_ALL            --turn on all airgens
  82. --SWITCH                --switch redstone output for a single controller
  83. --LABEL                 --just a sign { guiType = "LABEL", guiName = "Label Text", draw = { xPos = 1, yPos = 2, len = 10, textCol = colors.black, bgCol = colors.white } }
  84. --ENER_BARS             --bars: each is a sensor-controller { guiType = "ENER_BARS", guiName = "", controllerIds = { [1]="ENER_STOR_1", [2]="ENER_STOR_2", ...}, draw = { xPos = 1, yPos = 2, lenMultiplier = 2 } }
  85. --SENSOR                --shows its state, clicking on it sends state request { guiType = "SENSOR", guiNameRed = "fuel low", guiNameGreen = "fuel normal", controllerIds = {"SENSOR_FUEL"}, draw = { xPos = 1, yPos = 2, len = 10 } }
  86. --GUI_MODE_SWITCH       --switches gui mode between "MODE" and "default" { guiType = "GUI_MODE_SWITCH", guiName = "ver", guiMode = "MODE_VERSION", draw = { xPos = nil, yPos = 1, len = 10 } }
  87. --LASER_EM              --emitter info { guiType = "LASER_EM", guiName = "Lazor", controllerIds = {"LASER_P_1"}, draw = { xPos = 1, yPos = 2 }, laserData = { relX = 5, relY = 0, relZ = -3 } } --TODO update
  88. --TARGET_TABLE          --target table: length is fixed (17), uses global gui temporary table { draw = { xPos = 1, yPos = 2 }  } --TODO update description
  89.                         --T_guiTempData uses: targetList[numeric index] = {x,y,z,comment}
  90. --REFERENCE_POINT       --stores global coordinates of ship's origin, uses a Laser Emitter to get global coordinates. All widgets must have its ID in order to access them --TODO sdata description
  91.                         --T_guiTempData uses: origin = {x,y,z}
  92. --ENGAGE_BUTTON         --stores all packets for laser engagement and sends them when pushed
  93.                         --T_guiTempData uses: [laserControllerId] = {packet to send}
  94.  
  95.  
  96. -------------------         CONTROLLER TYPES (client-side)
  97. --P_                    --all peripheral-operating controllers MUST begin with this
  98. --P_LEM                 --laser emitter
  99. --CONTROLLER            --default type
  100.  
  101. --AIRLOCK               --two monitors, two redstone outputs
  102.  
  103.  
  104. -------------------         MODES
  105.  
  106. --MODE_DEFAULT          --default
  107. --MODE_VERSION          --shows controller software version (where possible)
  108. --MODE_UPDATE           --click on a widget sends an "update" message to all associated controllers instead of passing the click to processing function
  109. --MODE_OVERRIDE         --click on a widget sends an "override" message to all associated controllers instead of passing the click to processing function
  110. --MODE_GFOR             --show coordinates in global frame of reference where possible
  111. --MODE_LFOR             --show coordinates in local (ship-bound) frame of reference where possible
  112.  
  113.  
  114. -------------------         DEBUG LEVELS
  115. -- messageLevel <= sdata.settings.debugLvl - message goes to console TODO write all messages to logfile
  116. --0                     --important TODO make a widget to display
  117. --1                     --less important (unlisted controllers, unknown packets, ...)
  118. --2                     --trivial
  119.  
  120.  
  121.  
  122.  
  123. --TODO remove old description:----
  124. --TABLES IN MEMORY
  125. --T_T_guiXy: x,y -> id_gui
  126. --T_ctrlTempData: id_controller -> temporary data (current state, last update, etc) { state = color.green... }
  127.  
  128. --TABLES ON DISK -- deprecated -- all settings including GUI markup must be supplied in "sdata" file and loaded as an API
  129. --sdata.guiData: id_gui -> wat do on click, how to draw it, etc
  130. --sdata.ctrlData: id_controller -> type, data, id_gui
  131. --sdata.settings: server settings like monitor/modem side
  132.  
  133.  
  134. --PROPOSED BEHAVIOR:
  135. -- click event -> processClick(x,y) -> T_guiXy[x][y] -> id_gui -> sdata.guiData[id_gui].guiType -> ClickControllerType[id_gui] -> id_controller(s) -> messages
  136.  
  137. -- modem message event -> processMessage(message) -> id_controller -> sdata.ctrlData[id_controller]                             -> id_gui -> os.queueEvent("redraw", id_gui) -> drawGuiElement(id_gui)
  138. --                                                                 -> T_ctrlTempData[id_controller].state = message.state
  139. --                                                                 -> T_ctrlTempData[id_controller].lastResp = curTime
  140.  
  141. ----end of the old description----
  142.  
  143.  
  144. ---------------------------------------------------------------------------------------------------
  145. -------------------         GLOBALS
  146. ---------------------------------------------------------------------------------------------------
  147.  
  148.  
  149. --load data tables
  150. os.loadAPI("sdata")
  151.  
  152. --controller temporary data (must be populated according to messages received or sent)
  153. T_ctrlTempData = {}
  154.  
  155. --gui temporary data
  156. --TODO UNUSED?
  157. T_guiTempData = {}
  158.  
  159. --GUI click mapping table (must be populated in GUI render functions)
  160. T_guiXy = {}
  161.  
  162. --gui mode
  163. guiMode = "MODE_DEFAULT"
  164.  
  165. --modem
  166. modem = nil
  167. --touchscreen-capable monitor
  168. monitor = nil
  169. --monitor size
  170. mSizeX = nil
  171. mSizeY = nil
  172.  
  173.  
  174. ---------------------------------------------------------------------------------------------------
  175. -------------------         MAIN UTILS
  176. ---------------------------------------------------------------------------------------------------
  177.  
  178. --monitor&modem init
  179. function Init()
  180.     modem = peripheral.wrap(sdata.settings.modemSide)
  181.     while modem==nil do
  182.         PrintDbg("Wrapping modem...\n", 1)
  183.         sleep(1)
  184.         modem = peripheral.wrap(sdata.settings.modemSide)
  185.     end
  186.    
  187.     openChannel(modem, sdata.settings.channelReceive)
  188.    
  189.     monitor = peripheral.wrap(sdata.settings.monitorSide)
  190.     while monitor==nil do
  191.         PrintDbg("Wrapping monitor...\n", 1)
  192.         sleep(1)
  193.         monitor = peripheral.wrap(sdata.settings.monitorSide)
  194.     end
  195.     monitor.setTextScale(sdata.settings.textSize)
  196.     mSizeX, mSizeY = monitor.getSize()
  197.     PrintDbg("GPC server by drPepper", 0)
  198.     PrintDbg("Version "..VERSION_STRING, 0)
  199.     PrintDbg("Monitor size:"..tostring(mSizeX)..", "..tostring(mSizeY), 1)
  200.     monitor.write("GPC server by drPepper")
  201.     monitor.write("version "..VERSION_STRING)
  202.    
  203. end
  204.  
  205. --GUI input thread
  206. function GuiLoop()
  207.     --initial render
  208.     DrawAll()
  209.     while true do
  210.         local event, p1, p2, p3, p4, p5 = os.pullEvent()
  211.         if event == "monitor_touch" then
  212.             ProcessClick(p2,p3)
  213.         elseif event == "key" then
  214.             if p1 == 22 then
  215.                 print("press 1 to update only sdata, 2 to update everything")
  216.                 sleep(0.3)
  217.                 local event, p1, p2, p3, p4, p5  = os.pullEvent("key")
  218.                 if p1 == 2 then
  219.                     shell.run("rm", "sdata")
  220.                     shell.run("pastebin", "get "..sdata.settings.pastebinSData.." sdata")
  221.                     os.reboot()
  222.                 elseif p1 == 3 then
  223.                     shell.run("rm", "sdata")
  224.                     shell.run("pastebin", "get "..sdata.settings.pastebinSData.." sdata")
  225.                     shell.run("rm", "startup")
  226.                     shell.run("pastebin", "get "..sdata.settings.pastebin.." startup")
  227.                     os.reboot()
  228.                 end
  229.             end
  230.         end
  231.        
  232.     end
  233. end
  234.  
  235. --GUI redraw thread
  236. function RedrawLoop()
  237.     while true do
  238.         local event, p1 = os.pullEvent("redraw")
  239.         if p1~=nil then
  240.             for k,v in pairs (p1) do
  241.                 PrintDbg("redrawing "..tostring(v), 2)
  242.                 DrawWidget(v)
  243.             end
  244.         else
  245.             DrawAll()
  246.         end
  247.     end
  248. end
  249.  
  250. --message receiving/processing thread
  251. function ReceiveLoop()
  252.     while true do
  253.         local event, p1, p2, p3, p4, p5  = os.pullEvent("modem_message")
  254.         ProcessMessage(p4)
  255.     end
  256. end
  257.  
  258. ---------------------------------------------------------------------------------------------------
  259. -------------------         GENERAL UTILS
  260. ---------------------------------------------------------------------------------------------------
  261.  
  262. --serializes given table to a file using given or default name (if no given)
  263. --API compatible
  264. function WriteResume(tableName, fileName)
  265.     if tableName == nil then
  266.         PrintDbg("No table provided!", 0)
  267.         return
  268.     end
  269.     fileName = tostring(fileName)
  270.     if fileName == nil then
  271.         fileName = "resume"
  272.     end
  273.     local file = fs.open(fileName,"w")
  274.     local sT = textutils.serialize(tableName)
  275.     file.write(sT)
  276.     file.close()
  277.     return
  278. end
  279.  
  280.  
  281. --reads setting to a given table from a given or default file, writes default first if no file exists
  282. --API compatible
  283. function ReadResume(tableName, fileName)
  284.     if tableName == nil then
  285.         PrintDbg("No table provided!", 0)
  286.         return
  287.     end
  288.     fileName = tostring(fileName)
  289.     if fileName == nil then
  290.         fileName = "resume"
  291.     end
  292.     if not fs.exists(fileName) then
  293.         PrintDbg("Trying to resume without resume file, creating default: "..fileName, 1)
  294.         WriteResume(tableName, fileName)
  295.     end
  296.     local file = fs.open(fileName,"r")
  297.     local sT = file.readAll()
  298.     tableName = textutils.unserialize(sT)
  299.     file.close()
  300.     return tableName
  301. end
  302.  
  303. --opens a channel
  304. --API compatible
  305. function openChannel(modem, n)
  306.     if modem == nil then
  307.         PrintDbg("no modem present", 0)
  308.         return
  309.     end
  310.     modem.open(n)
  311.     while modem.isOpen(n)== false do
  312.         PrintDbg("opening channel "..n.."...\n", 1)
  313.         sleep(1)
  314.         modem.open(n)
  315.     end
  316. end
  317.  
  318.  
  319. --kiloticks in 24hr format as args
  320. --API compatible
  321. function KiloTicksPassed(from, to)
  322.     if (from > to) then
  323.         --midnight
  324.         to = to+24.0  
  325.     end
  326.     return (to-from)
  327. end
  328.  
  329.  
  330. --debug messages
  331. function PrintDbg(message, level)
  332.     if level == nil then
  333.         level = 1
  334.     end
  335.     if (level <= sdata.settings.debugLvl) then
  336.         print("D:"..message)
  337.     end
  338. end
  339.  
  340. --converts from Minecraft Cartesian to Polar (zenith angle from y axis to r, azimuth from z axis to r projection)
  341. --returns radial distance, zenith angle and azimuth angle in radians
  342. --zenith angle varies from 0 to pi, azimuth angle varies from -pi to pi, thanks to math.atan2()
  343. function CartesianToPolar(x, y, z)
  344.     if x== nil or y==nil or z==nil then
  345.         return nil, nil, nil
  346.     end
  347.  
  348.     x = tonumber(x)
  349.     y = tonumber(y)
  350.     z = tonumber(z)
  351.     local r = math.sqrt(x*x + y*y +z*z)
  352.     if r == 0 then
  353.         return 0, 0, 0
  354.     end
  355.  
  356.     local theta = math.acos( y/r )
  357.     local phi = math.atan2(x, z)
  358.     return r, theta, phi
  359. end
  360.  
  361. ---------------------------------------------------------------------------------------------------
  362. -------------------         NETWORK HI-LEVEL
  363. ---------------------------------------------------------------------------------------------------
  364.  
  365. --sends status request to the client
  366. function SendStatusRequest(target)
  367.     local packetT =
  368.     {
  369.         target = target,
  370.         command = "REPORT",
  371.     }
  372.  
  373.     PrintDbg("sending REPORT to "..target, 2)
  374.     local packet = textutils.serialize(packetT)
  375.     modem.transmit(sdata.settings.channelSend, sdata.settings.channelReceive, packet)
  376. end
  377.  
  378.  
  379. function SendPosRequest(target)
  380.     local packetT =
  381.     {
  382.         target = target,
  383.         command = "PEXECUTE",
  384.         pCommand = "pos"
  385.     }
  386.     PrintDbg("sending PEXECUTE pos to "..target, 2)
  387.     local packet = textutils.serialize(packetT)
  388.     modem.transmit(sdata.settings.channelSend, sdata.settings.channelReceive, packet)
  389. end
  390.  
  391.  
  392. --stores received data and posts redraw event
  393. function ProcessMessage(packet)
  394.     local curTime = os.time()
  395.     local packetT = textutils.unserialize(packet)
  396.     if packetT.sender ~= nil and packetT.state ~= nil then
  397.         PrintDbg("Received "..tostring(packetT.state).." from "..tostring(packetT.sender), 2)
  398.         if T_ctrlTempData[tostring(packetT.sender)] ~= nil then
  399.             T_ctrlTempData[tostring(packetT.sender)].state = packetT.state
  400.             T_ctrlTempData[tostring(packetT.sender)].lastResp = curTime
  401.             T_ctrlTempData[tostring(packetT.sender)].version = packetT.version
  402.             T_ctrlTempData[tostring(packetT.sender)].override = packetT.override
  403.             --Peripheral processing
  404.             if string.sub(packetT.controllerType, 1, 2) == "P_" then
  405.                 ProcessPeripheralReturnMessage(packetT)
  406.             end
  407.         end
  408.         if sdata.ctrlData[packetT.sender] ~= nil then
  409.             PrintDbg("posting redraw event for "..tostring(packetT.sender), 2)
  410.            
  411.             os.queueEvent("redraw", sdata.ctrlData[packetT.sender].guiIds)
  412.         else
  413.             PrintDbg("ProcessMessage(): no ctrlData for "..tostring(packetT.sender), 2)
  414.         end
  415.     end
  416.    
  417. end
  418.  
  419.  
  420. --processes data returned by a peripheral.call() method executed on a remote controller
  421. --max of 6 returned params are supported, for the support of different peripheral types see the code
  422. function ProcessPeripheralReturnMessage(packetT)
  423.     if packetT.pExdCommand == nil then
  424.         PrintDbg("no pExdCommand in the incoming packet", 2)
  425.         return
  426.     end
  427.    
  428.     if T_ctrlTempData[tostring(packetT.sender)] == nil then
  429.         T_ctrlTempData[tostring(packetT.sender)] = {}
  430.     end
  431.        
  432.     T_ctrlTempData[tostring(packetT.sender)][tostring(packetT.pExdCommand)] = { [1] = packetT.p1,
  433.                                                                                 [2] = packetT.p2,
  434.                                                                                 [3] = packetT.p3,
  435.                                                                                 [4] = packetT.p4,
  436.                                                                                 [5] = packetT.p5,
  437.                                                                                 [6] = packetT.p6 }
  438.     PrintDbg("ec: "..tostring(packetT.pExdCommand), 2)
  439.     PrintDbg("p1: "..tostring(packetT.p1), 2)
  440.     PrintDbg("p2: "..tostring(packetT.p2), 2)
  441.     PrintDbg("p3: "..tostring(packetT.p3), 2)
  442.     PrintDbg("p4: "..tostring(packetT.p4), 2)
  443.     PrintDbg("p5: "..tostring(packetT.p5), 2)
  444.     PrintDbg("p6: "..tostring(packetT.p6), 2)
  445. end
  446.  
  447.  
  448. ---------------------------------------------------------------------------------------------------
  449. -------------------         WIDGET DRAWING
  450. ---------------------------------------------------------------------------------------------------
  451.  
  452. --draw all widgets from sdata
  453. function DrawAll()
  454.     monitor.setBackgroundColor(sdata.settings.guiBgColor)
  455.     monitor.clear()
  456.     monitor.setCursorPos(1,1)
  457.     for k,v in pairs (sdata.guiData) do
  458.         PrintDbg("drawing "..tostring(k), 2)
  459.         DrawWidget(k)
  460.     end
  461. end
  462.  
  463. --chooses proper drawing function by id
  464. function DrawWidget(guiDataId)
  465.     if sdata.guiData[guiDataId] == nil or sdata.guiData[guiDataId].guiType == nil then
  466.         PrintDbg("DrawWidget() error: "..tostring(guiDataId), 2)
  467.         return
  468.     end
  469.    
  470.     local controllerType = sdata.guiData[guiDataId].guiType
  471.    
  472.     if controllerType == "SWITCH" then
  473.         DrawWidgetSwitch(guiDataId)
  474.     elseif controllerType == "AIRGEN_ALL" then
  475.         DrawWidgetAGAll(guiDataId)
  476.     elseif controllerType == "LABEL" then
  477.         DrawWidgetLabel(guiDataId)
  478.     elseif controllerType == "SENSOR" then
  479.         DrawWidgetSensor(guiDataId)
  480.     elseif controllerType == "ENER_BARS" then
  481.         DrawWidgetSensorBar(guiDataId)
  482.     elseif controllerType == "LASER_EM" then
  483.         DrawWidgetLaserEm(guiDataId)
  484.     elseif controllerType == "GUI_MODE_SWITCH" then
  485.         DrawWidgetGuiModeSwitch(guiDataId)
  486.     elseif controllerType == "TARGET_TABLE" then
  487.         DrawWidgetTargetTable(guiDataId)
  488.     elseif controllerType == "REFERENCE_POINT" then
  489.         DrawWidgetReferencePoint(guiDataId)
  490.     elseif controllerType == "ENGAGE_BUTTON" then
  491.         DrawWidgetEngageButton(guiDataId)
  492.     else
  493.         PrintDbg("DrawWidget(): unknown type: "..tostring(sdata.guiData[guiDataId].guiType), 2)
  494.     end
  495.    
  496. end
  497.  
  498. --for simple on/off controllers, which state is depicted by color string, only first controllerId is used
  499. function DrawWidgetSwitch(guiDataId)
  500.     local widget = sdata.guiData[guiDataId]
  501.     if widget.draw.xPos == nil or widget.draw.yPos == nil then
  502.         PrintDbg("DrawWidgetSwitch() data missing", 2)
  503.         return
  504.     end
  505.     local yPosition = tonumber(widget.draw.yPos)
  506.     local xPosition = tonumber(widget.draw.xPos)
  507.     local widgetText = guiDataId
  508.  
  509.     if widget.guiName ~= nil and widget.guiName ~= "" then
  510.         widgetText = widget.guiName
  511.     end
  512.    
  513.     monitor.setCursorPos(xPosition, yPosition)
  514.     monitor.setTextColor(sdata.settings.guiTextColor)
  515.    
  516.     local curTime = os.time()
  517.     local controllerId = widget.controllerIds[1]
  518.     if controllerId == nil then
  519.         PrintDbg("DrawWidgetSwitch() controllerId missing", 2)
  520.         return
  521.     end
  522.     local missing = false
  523.    
  524.     if T_ctrlTempData[controllerId] == nil then
  525.         T_ctrlTempData[controllerId] = {}
  526.         T_ctrlTempData[controllerId].state = colors.orange
  527.         T_ctrlTempData[controllerId].lastResp = 0
  528.     end
  529.    
  530.     local lastTime = T_ctrlTempData[controllerId].lastResp
  531.     if lastTime == nil then
  532.         lastTime = 0
  533.     end
  534.    
  535.     --checking if missing
  536.     if KiloTicksPassed(lastTime, curTime) > sdata.settings.statusUpdate then
  537.         --sending status request
  538.         SendStatusRequest(controllerId)    
  539.     end
  540.     if KiloTicksPassed(lastTime, curTime) > sdata.settings.statusTimeout then
  541.         --marking as missing
  542.         missing = true
  543.     end
  544.    
  545.     if missing then
  546.         monitor.setBackgroundColor(colors.orange)
  547.     else
  548.         monitor.setBackgroundColor(T_ctrlTempData[controllerId].state)
  549.     end
  550.    
  551.     local maxX = mSizeX
  552.     if widget.draw.len ~= nil then
  553.         maxX = xPosition + widget.draw.len
  554.     end
  555.    
  556.     if guiMode == "MODE_VERSION" then
  557.         widgetText = tostring(T_ctrlTempData[controllerId].version)
  558.     elseif guiMode == "MODE_OVERRIDE" then
  559.         widgetText = tostring(T_ctrlTempData[controllerId].override)
  560.     end
  561.    
  562.     for i = xPosition, maxX do
  563.         local letterPos = i + 1 - xPosition
  564.         if letterPos <= string.len(widgetText) then
  565.             monitor.write(string.sub(widgetText, letterPos, letterPos))
  566.         else
  567.             monitor.write(" ")
  568.         end
  569.        
  570.         if T_guiXy[i] == nil then
  571.             T_guiXy[i] = {}
  572.         end
  573.         T_guiXy[i][yPosition] = guiDataId
  574.     end
  575. end
  576.  
  577. function DrawWidgetAGAll(guiDataId)
  578.     if sdata.guiData[guiDataId].draw.yPos == nil then
  579.         PrintDbg("DrawWidgetAGAll error", 2)
  580.         return
  581.     end
  582.     local yPosition = tonumber(sdata.guiData[guiDataId].draw.yPos)
  583.     local xPosition = tonumber(mSizeX - sdata.settings.guiRightColWidth)
  584.     monitor.setCursorPos(xPosition, yPosition)
  585.     monitor.setTextColor(sdata.settings.guiTextColor)
  586.     monitor.setBackgroundColor(colors.gray)
  587.     monitor.write("ALL AG")
  588.     if T_guiXy[xPosition] == nil then
  589.         T_guiXy[xPosition] = {}
  590.     end
  591.     for i = xPosition, mSizeX do
  592.         if T_guiXy[i] == nil then
  593.             T_guiXy[i] = {}
  594.         end
  595.         T_guiXy[i][yPosition] = guiDataId
  596.     end
  597. end
  598.  
  599.  
  600. function DrawWidgetLabel(guiDataId)
  601.     local label = sdata.guiData[guiDataId]
  602.     if label.draw.yPos == nil or label.draw.xPos == nil then
  603.         PrintDbg("DrawWidgetLabel() error", 2)
  604.         return
  605.     end
  606.     local yPosition = tonumber(label.draw.yPos)
  607.     local xPosition = tonumber(label.draw.xPos)
  608.     monitor.setCursorPos(xPosition, yPosition)
  609.     if label.draw.textCol ~= nil then
  610.         monitor.setTextColor(label.draw.textCol)
  611.     end
  612.     if label.draw.bgCol ~= nil then
  613.         monitor.setBackgroundColor(label.draw.bgCol)
  614.     end
  615.    
  616.     local maxX = mSizeX
  617.     if label.draw.len ~= nil then
  618.         maxX = xPosition + label.draw.len
  619.     end
  620.    
  621.     for i = xPosition, maxX do
  622.         local letterPos = i + 1 - xPosition
  623.         if letterPos <= string.len(label.guiName) then
  624.             monitor.write(string.sub(label.guiName, letterPos, letterPos))
  625.         else
  626.             monitor.write(" ")
  627.         end
  628.        
  629.         if T_guiXy[i] == nil then
  630.             T_guiXy[i] = {}
  631.         end
  632.         T_guiXy[i][label.draw.yPos] = guiDataId
  633.     end
  634. end
  635.  
  636.  
  637. function DrawWidgetSensor(guiDataId)
  638.     local sensor = sdata.guiData[guiDataId]
  639.     if sensor.draw.xPos == nil or sensor.draw.yPos == nil then
  640.         PrintDbg("DrawWidgetSensor() data missing", 2)
  641.         return
  642.     end
  643.     local yPosition = tonumber(sensor.draw.yPos)
  644.     local xPosition = tonumber(sensor.draw.xPos)
  645.     monitor.setCursorPos(xPosition, yPosition)
  646.     monitor.setTextColor(sdata.settings.guiTextColor)
  647.     local curTime = os.time()
  648.    
  649.     local sensorId = sensor.controllerIds[1]
  650.     if sensorId == nil then
  651.         PrintDbg("DrawWidgetSensor() sensorId missing", 2)
  652.         return
  653.     end
  654.     local missing = false
  655.    
  656.     if T_ctrlTempData[sensorId] == nil then
  657.         T_ctrlTempData[sensorId] = { state = colors.orange, lastResp = 0 }
  658.     end
  659.    
  660.     local lastTime = T_ctrlTempData[sensorId].lastResp
  661.     if lastTime == nil then
  662.         lastTime = 0
  663.     end
  664.    
  665.     --checking if missing
  666.     if KiloTicksPassed(lastTime, curTime) > sdata.settings.statusUpdate then
  667.         --sending status request
  668.         SendStatusRequest(sensorId)
  669.     end
  670.     if KiloTicksPassed(lastTime, curTime) > sdata.settings.statusTimeout then
  671.         --marking as missing
  672.         missing = true
  673.     end
  674.    
  675.     if missing then
  676.         monitor.setBackgroundColor(colors.orange)
  677.     else
  678.         monitor.setBackgroundColor(T_ctrlTempData[sensorId].state)
  679.     end
  680.    
  681.     local maxX = mSizeX
  682.     if sensor.draw.len ~= nil then
  683.         maxX = xPosition + sensor.draw.len
  684.     end
  685.    
  686.     local sensorText = sensorId
  687.    
  688.     if T_ctrlTempData[sensorId].state == colors.green then
  689.         sensorText = sensor.guiNameGreen
  690.     elseif T_ctrlTempData[sensorId].state == colors.red then
  691.         sensorText = sensor.guiNameRed
  692.     end
  693.    
  694.     if guiMode == "MODE_VERSION" then
  695.         sensorText = T_ctrlTempData[sensorId].version
  696.     elseif guiMode == "MODE_OVERRIDE" then
  697.         sensorText = tostring(T_ctrlTempData[sensorId].override)
  698.     end
  699.    
  700.     sensorText = tostring(sensorText)
  701.    
  702.     for i = xPosition, maxX do
  703.         local letterPos = i + 1 - xPosition
  704.         if letterPos <= string.len(sensorText) then
  705.             monitor.write(string.sub(sensorText, letterPos, letterPos))
  706.         else
  707.             monitor.write(" ")
  708.         end
  709.        
  710.         if T_guiXy[i] == nil then
  711.             T_guiXy[i] = {}
  712.         end
  713.         T_guiXy[i][yPosition] = guiDataId
  714.     end
  715.  
  716. end
  717.  
  718.  
  719. function DrawWidgetSensorBar(guiDataId)
  720.     local bar = sdata.guiData[guiDataId]
  721.     if bar.draw.xPos == nil or bar.draw.yPos == nil then
  722.         PrintDbg("DrawWidgetSensorBar() data missing", 2)
  723.         return
  724.     end
  725.     monitor.setCursorPos(bar.draw.xPos, bar.draw.yPos)
  726.     monitor.setTextColor(sdata.settings.guiTextColor)
  727.     local curTime = os.time()
  728.    
  729.     for index=1, #bar.controllerIds do
  730.         local sensorId = bar.controllerIds[index]
  731.         if sensorId == nil then
  732.             PrintDbg("DrawWidgetSensorBar() sensorId missing", 2)
  733.             return
  734.         end
  735.         local missing = false
  736.    
  737.         if T_ctrlTempData[sensorId] == nil then
  738.             T_ctrlTempData[sensorId] = { state = colors.orange, lastResp = 0 }
  739.         end
  740.    
  741.         local lastTime = T_ctrlTempData[sensorId].lastResp
  742.         if lastTime == nil then
  743.             lastTime = 0
  744.         end
  745.    
  746.         --checking if missing
  747.         if KiloTicksPassed(lastTime, curTime) > sdata.settings.statusUpdate then
  748.             --sending status request
  749.             SendStatusRequest(sensorId)
  750.         end
  751.         if KiloTicksPassed(lastTime, curTime) > sdata.settings.statusTimeout then
  752.             --marking as missing
  753.             missing = true
  754.         end
  755.    
  756.         if missing then
  757.             monitor.setBackgroundColor(colors.orange)
  758.         else
  759.             monitor.setBackgroundColor(T_ctrlTempData[sensorId].state)
  760.         end
  761.    
  762.         local len = 1
  763.         if bar.draw.lenMultiplier ~= nil then
  764.             len = bar.draw.lenMultiplier
  765.         end
  766.    
  767.         monitor.setTextColor(sdata.settings.guiTextColor)
  768.         for i = bar.draw.xPos + (index - 1)*len, bar.draw.xPos + index*len - 1 do
  769.             monitor.write(" ")
  770.        
  771.             if T_guiXy[i] == nil then
  772.                 T_guiXy[i] = {}
  773.             end
  774.             T_guiXy[i][bar.draw.yPos] = guiDataId
  775.         end
  776.     end
  777. end
  778.  
  779.  
  780. function DrawWidgetGuiModeSwitch(guiDataId)
  781.     local widget = sdata.guiData[guiDataId]
  782.     if widget.draw.len == nil or widget.draw.yPos == nil or widget.guiName == nil then
  783.         PrintDbg("DrawWidgetGuiModeSwitch() data missing", 2)
  784.         return
  785.     end
  786.     xPosition = 1
  787.     if widget.draw.xPos == nil then
  788.         xPosition = mSizeX - widget.draw.len
  789.     else
  790.         xPosition = widget.draw.xPos
  791.     end
  792.     monitor.setCursorPos(xPosition, widget.draw.yPos)
  793.     monitor.setTextColor(sdata.settings.guiBgColor)
  794. --  PrintDbg("DrawWidgetGuiModeSwitch(): "..tostring(guiDataId)..", cur mode:"..tostring(guiMode).."wid mode:"..tostring(sdata.guiData[guiDataId].guiMode), 0)
  795.     if guiMode == sdata.guiData[guiDataId].guiMode then
  796.         monitor.setBackgroundColor(colors.cyan)
  797.     else
  798.         monitor.setBackgroundColor(colors.lightBlue)
  799.     end
  800.     for i = xPosition, xPosition + widget.draw.len do
  801.         local letterPos = i + 1 - xPosition
  802.         if letterPos <= string.len(widget.guiName) then
  803.             monitor.write(string.sub(widget.guiName, letterPos, letterPos))
  804.         else
  805.             monitor.write(" ")
  806.         end
  807.        
  808.         if T_guiXy[i] == nil then
  809.             T_guiXy[i] = {}
  810.         end
  811.         T_guiXy[i][widget.draw.yPos] = guiDataId
  812.     end
  813. end
  814.  
  815.  
  816. --draws data for primary emitters. Displays r, theta, phi if there is a selected target or its widget text if none is selected.
  817. --first symbol background displays state of the controller(red/green/orange), other symbols - the ability to emit beam in the direction of the target selected (red/green)
  818. function DrawWidgetLaserEm(guiDataId)
  819.     PrintDbg("Entering DrawWidgetLaserEm()", 1)
  820.     local widget = sdata.guiData[guiDataId]
  821.     if widget.draw.xPos == nil or widget.draw.yPos == nil or widget.rPGuiId == nil then
  822.         PrintDbg("DrawWidgetLaserEm() data missing", 1)
  823.         return
  824.     end
  825.     local yPosition = tonumber(widget.draw.yPos)
  826.     local xPosition = tonumber(widget.draw.xPos)
  827.     monitor.setCursorPos(xPosition, yPosition)
  828.     monitor.setTextColor(sdata.settings.guiTextColor)
  829.    
  830.     local widgetText = guiDataId
  831.     if widget.guiName ~= nil and widget.guiName ~= "" then
  832.         widgetText = widget.guiName
  833.     end
  834.    
  835.     local curTime = os.time()
  836.    
  837.     local controllerId = widget.controllerIds[1]
  838.     if controllerId == nil then
  839.         PrintDbg("DrawWidgetLaserEm() sensorId missing", 1)
  840.         return
  841.     end
  842.    
  843.     if T_ctrlTempData[controllerId] == nil then
  844.         PrintDbg("DrawWidgetLaserEm() T_ctrlTempData missing", 1)
  845.         T_ctrlTempData[controllerId] = {}
  846.         T_ctrlTempData[controllerId].state = colors.orange
  847.         T_ctrlTempData[controllerId].lastResp = 0
  848.         return
  849.     end
  850.    
  851.     if T_ctrlTempData[controllerId].pos == nil then
  852.         PrintDbg("DrawWidgetReferencePoint() 'pos' result missing", 1)
  853.         T_ctrlTempData[controllerId].pos = {}
  854.         SendPosRequest(controllerId)
  855.         return
  856.     end
  857.     local missing = false
  858.    
  859.     local lastTime = T_ctrlTempData[controllerId].lastResp
  860.     if lastTime == nil then
  861.         lastTime = 0
  862.     end
  863.        
  864.     --checking if missing
  865.     if KiloTicksPassed(lastTime, curTime) > sdata.settings.statusUpdate then
  866.         --sending status request
  867.         PrintDbg("DrawWidgetLaserEm(): status req", 1)
  868.         SendPosRequest(controllerId)
  869.     end
  870.     if KiloTicksPassed(lastTime, curTime) > sdata.settings.statusTimeout then
  871.         --marking as missing
  872.         missing = true
  873.     end
  874.    
  875.     if missing then
  876.         monitor.setBackgroundColor(colors.orange)
  877.     else
  878.         monitor.setBackgroundColor(T_ctrlTempData[controllerId].state)
  879.     end
  880.    
  881.     --emitter coordinates
  882.     if T_ctrlTempData[controllerId].pos[1] == nil or T_ctrlTempData[controllerId].pos[2] == nil or T_ctrlTempData[controllerId].pos[3] == nil then
  883.         PrintDbg("DrawWidgetLaserEm() 'pos' result is nil", 1)
  884.         SendPosRequest(controllerId)
  885.         return
  886.     end
  887.    
  888.     gx = tonumber(T_ctrlTempData[controllerId].pos[1])
  889.     gy = tonumber(T_ctrlTempData[controllerId].pos[2])
  890.     gz = tonumber(T_ctrlTempData[controllerId].pos[3])
  891.  
  892.     if guiMode == "MODE_VERSION" then
  893.         widgetText = tostring(T_ctrlTempData[controllerId].version)
  894.     elseif guiMode == "MODE_GFOR" then
  895.         widgetText = tostring(gx)..";"..tostring(gy)..";"..tostring(gz)..";"
  896.     end
  897.    
  898.     --target vector
  899.     local tx, ty, tz = nil, nil, nil
  900.     local selected = T_guiTempData[widget.tTGuiId].selected
  901.     if selected ~= nil and T_guiTempData[widget.tTGuiId].targetList~= nil and T_guiTempData[widget.tTGuiId].targetList[selected]~= nil then
  902.         tx = tonumber(T_guiTempData[widget.tTGuiId].targetList[selected].x) - gx
  903.         ty = tonumber(T_guiTempData[widget.tTGuiId].targetList[selected].y) - gy
  904.         tz = tonumber(T_guiTempData[widget.tTGuiId].targetList[selected].z) - gz
  905.     end
  906.    
  907.     local r, t, p = CartesianToPolar(tx, ty, tz)
  908.  
  909.     local canFire = false
  910.  
  911.     if T_guiTempData[widget.eBGuId] == nil then
  912.         T_guiTempData[widget.eBGuId] = {}
  913.     end
  914.    
  915.     if r ~= nil or t~=nil or p ~= nil then
  916.         t = math.deg(t)
  917.         p = math.deg(p)
  918.         PrintDbg(tostring(r)..";"..tostring(t)..";"..tostring(p), 1)
  919.        
  920.         local allowed = sdata.ctrlData[controllerId].allowed
  921.         --allowed = { { { t, t }, {p, p} }, { { t, t }, {p, p} } }
  922.        
  923.         for i=1, table.getn(allowed) do
  924.             if t >= allowed[i][1][1] and t<= allowed[i][1][2] then
  925.                 PrintDbg(tostring(t).." between "..tostring(allowed[i][1][1])..";"..tostring(allowed[i][1][2]), 2)
  926.                 if t == 0 or t == 180 then  --gimbal lock workaround
  927.                     canFire = true
  928.                 else
  929.                     if p >= allowed[i][2][1] and p<= allowed[i][2][2] then
  930.                         PrintDbg(tostring(t).." between "..tostring(allowed[i][2][1])..";"..tostring(allowed[i][2][2]), 2)
  931.                         canFire = true
  932.                     end
  933.                 end
  934.             end
  935.         end
  936.        
  937.         widgetText = string.format("%.1f;%.1f;%.1f", r, t, p)      
  938.  
  939.         if canFire and T_ctrlTempData[controllerId].state == colors.green then
  940.             --create a packet to send when FIRE button is pressed
  941.             local packetT =
  942.             {
  943.                 target = controllerId,
  944.                 command = "PEXECUTE",
  945.                 pCommand = "emitBeam",
  946.                 p1 = tx,
  947.                 p2 = ty,
  948.                 p3 = - tz, --AZAZA CROS CANNOT INTO TRANSFORMATIONS WORKAROUND
  949.                 delay = sdata.settings.laserDelay
  950.             }
  951.            
  952.             --packets for secondary lasers
  953.             local secondary = sdata.ctrlData[controllerId].secondary
  954.             for i=1, table.getn(secondary) do
  955.                 --emitter coordinates
  956.                 skipEmitter = false
  957.  
  958.                 if T_ctrlTempData[secondary[i]] == nil then
  959.                     T_ctrlTempData[secondary[i]] = {}
  960.                     SendPosRequest(controllerId)
  961.                     skipEmitter = true
  962.                 end
  963.  
  964.                 if T_ctrlTempData[secondary[i]].pos == nil then
  965.                     PrintDbg("DrawWidgetLaserEm() 'pos' result missing for "..tostring(secondary[i]), 1)
  966.                     T_ctrlTempData[secondary[i]].pos = {}
  967.                     SendPosRequest(secondary[i])
  968.                     skipEmitter = true
  969.                 end
  970.                 if T_ctrlTempData[secondary[i]].pos[1] == nil or T_ctrlTempData[secondary[i]].pos[2] == nil or T_ctrlTempData[secondary[i]].pos[3] == nil then
  971.                     PrintDbg("DrawWidgetLaserEm() 'pos' result is nil for "..tostring(secondary[i]), 1)
  972.                     SendPosRequest(secondary[i])
  973.                     skipEmitter = true
  974.                 end
  975.                 if skipEmitter == false then
  976.                     sgx = tonumber(T_ctrlTempData[secondary[i]].pos[1])
  977.                     sgy = tonumber(T_ctrlTempData[secondary[i]].pos[2])
  978.                     sgz = tonumber(T_ctrlTempData[secondary[i]].pos[3])
  979.  
  980.                     local sPacketT =
  981.                     {
  982.                         target = secondary[i],
  983.                         command = "PEXECUTE",
  984.                         pCommand = "emitBeam",
  985.                         p1 = gx - sgx,
  986.                         p2 = gy - sgy,
  987.                         p3 = - gz + sgz, --AZAZA CROS CANNOT INTO TRANSFORMATIONS WORKAROUND
  988.                     }
  989.                     T_guiTempData[widget.eBGuId][secondary[i]] = textutils.serialize(sPacketT)
  990.                 end
  991.             end
  992.            
  993.             T_guiTempData[widget.eBGuId][controllerId] = textutils.serialize(packetT)
  994.         else
  995.             T_guiTempData[widget.eBGuId][controllerId] = nil
  996.         end
  997.     else
  998.         T_guiTempData[widget.eBGuId][controllerId] = nil
  999.     end
  1000.        
  1001.    
  1002.     for i = xPosition, xPosition + widget.draw.len do
  1003.         local letterPos = i + 1 - xPosition
  1004.         if letterPos > 3 then
  1005.             if canFire then
  1006.                 monitor.setBackgroundColor(colors.green)
  1007.             else
  1008.                 monitor.setBackgroundColor(colors.red)
  1009.             end
  1010.         end
  1011.         if letterPos <= string.len(widgetText) then
  1012.             monitor.write(string.sub(widgetText, letterPos, letterPos))
  1013.         else
  1014.             monitor.write(" ")
  1015.         end
  1016.        
  1017.         if T_guiXy[i] == nil then
  1018.             T_guiXy[i] = {}
  1019.         end
  1020.         T_guiXy[i][yPosition] = guiDataId
  1021.     end
  1022. end
  1023.  
  1024.  
  1025. function DrawWidgetTargetTable(guiDataId)
  1026.     local widget = sdata.guiData[guiDataId]
  1027.     if widget.draw.xPos == nil or widget.draw.yPos == nil then
  1028.         PrintDbg("DrawWidgetLaserTargetTable() data missing", 2)
  1029.         return
  1030.     end
  1031.    
  1032.     if T_guiTempData[guiDataId] == nil then
  1033.         T_guiTempData[guiDataId] = {}
  1034.     end
  1035.    
  1036.     if T_guiTempData[guiDataId].targetList == nil then
  1037.         PrintDbg("DrawWidgetLaserTargetTable(): Resuming from file...", 1)
  1038.         T_guiTempData[guiDataId].targetList = {}
  1039.         T_guiTempData[guiDataId].targetList = ReadResume(T_guiTempData[guiDataId].targetList, "ldata")
  1040.     end
  1041.    
  1042.    
  1043.     local yPosition = tonumber(widget.draw.yPos)
  1044.     local xPosition = tonumber(widget.draw.xPos)
  1045.  
  1046.     local maxX = xPosition + widget.draw.len
  1047.     if maxX > mSizeX then
  1048.         maxX = mSizeX
  1049.     end
  1050.     local maxY = mSizeY         --to the bottom of the screen
  1051.    
  1052.     --converting to LFOR
  1053.     local originX, originY, originZ = 0.0, 0.0, 0.0
  1054.     if guiMode == "MODE_LFOR" then
  1055.         if T_guiTempData[widget.rPGuiId]~= nil and T_guiTempData[widget.rPGuiId].origin ~= nil then
  1056.             originX, originY, originZ = T_guiTempData[widget.rPGuiId].origin.x, T_guiTempData[widget.rPGuiId].origin.y, T_guiTempData[widget.rPGuiId].origin.z
  1057.         end
  1058.     end
  1059.        
  1060.     --lines from last to first
  1061.     for iy = yPosition, maxY - 1 do
  1062.         local index = table.getn(T_guiTempData[guiDataId].targetList) + yPosition - iy
  1063.  
  1064.         monitor.setBackgroundColor(colors.lightGray)
  1065.         monitor.setTextColor(sdata.settings.guiTextColor)
  1066.  
  1067.         if T_guiTempData[guiDataId].selected ~= nil then
  1068.             if index == T_guiTempData[guiDataId].selected then
  1069.                 monitor.setBackgroundColor(colors.gray)
  1070.             end
  1071.         end
  1072.        
  1073.         local textLine = ""
  1074.         if index > 0 then
  1075.             local tx = tonumber(T_guiTempData[guiDataId].targetList[index].x) - originX
  1076.             local ty = tonumber(T_guiTempData[guiDataId].targetList[index].y) - originY
  1077.             local tz = tonumber(T_guiTempData[guiDataId].targetList[index].z) - originZ
  1078.            
  1079.             textLine = tostring(tx)..";"..tostring(ty)..";"..tostring(tz)..";"..tostring(T_guiTempData[guiDataId].targetList[index].comment)
  1080.         else
  1081.             textLine = "-"
  1082.         end
  1083.        
  1084.         for ix = xPosition, maxX do
  1085.             local letterPos = ix + 1 - xPosition
  1086.             monitor.setCursorPos(ix, iy)
  1087.             if letterPos <= string.len(textLine) then
  1088.                 monitor.write(string.sub(textLine, letterPos, letterPos))
  1089.             else
  1090.                 monitor.write(" ")
  1091.             end
  1092.        
  1093.             if T_guiXy[ix] == nil then
  1094.                 T_guiXy[ix] = {}
  1095.             end
  1096.             T_guiXy[ix][iy] = guiDataId
  1097.         end
  1098.     end
  1099.    
  1100.     --buttons
  1101.     monitor.setCursorPos(xPosition, maxY)
  1102.     monitor.setBackgroundColor(colors.green)
  1103.     monitor.setTextColor(sdata.settings.guiBgColor)
  1104.     monitor.write(" ADD ") --x 1 to 5
  1105.     monitor.setBackgroundColor(sdata.settings.guiBgColor)
  1106.     monitor.write(" ") --x 6
  1107.     monitor.setBackgroundColor(colors.red)
  1108.     monitor.write("-") --x 7
  1109.     monitor.setBackgroundColor(sdata.settings.guiBgColor)
  1110.     monitor.write(" ") --x 8
  1111.     monitor.setBackgroundColor(colors.green)
  1112.     monitor.setTextColor(sdata.settings.guiBgColor)
  1113.     monitor.write(" SAVE ") --x 9 to 14
  1114.     for ix = xPosition, maxX do
  1115.         T_guiXy[ix][maxY] = guiDataId
  1116.     end
  1117. end
  1118.  
  1119.  
  1120. --reference point: global or local coordinates of a ship's origin obtained from a dedicated (or not) laser emitter controller
  1121. function DrawWidgetReferencePoint(guiDataId)
  1122.     local widget = sdata.guiData[guiDataId]
  1123.     if widget.draw.xPos == nil or widget.draw.yPos == nil then
  1124.         PrintDbg("DrawWidgetReferencePoint() data missing", 1)
  1125.         return
  1126.     end
  1127.     local yPosition = tonumber(widget.draw.yPos)
  1128.     local xPosition = tonumber(widget.draw.xPos)
  1129.    
  1130.     monitor.setCursorPos(xPosition, yPosition)
  1131.     monitor.setTextColor(sdata.settings.guiTextColor)
  1132.    
  1133.     --controller check
  1134.     local curTime = os.time()
  1135.     local controllerId = widget.controllerIds[1]
  1136.     if controllerId == nil then
  1137.         PrintDbg("DrawWidgetReferencePoint() controllerId missing", 1)
  1138.         return
  1139.     end
  1140.    
  1141.     local lx, ly, lz = sdata.ctrlData[controllerId].lx, sdata.ctrlData[controllerId].ly, sdata.ctrlData[controllerId].lz
  1142.     if lx == nil or ly == nil or lz == nil then
  1143.         PrintDbg("DrawWidgetReferencePoint() local coordinate data missing", 1)
  1144.         return
  1145.     end
  1146.  
  1147.    
  1148.     local missing = false
  1149.    
  1150.     if T_ctrlTempData[controllerId] == nil then
  1151.         T_ctrlTempData[controllerId] = { state = colors.orange, lastResp = 0 }
  1152.     end
  1153.    
  1154.     local lastTime = T_ctrlTempData[controllerId].lastResp
  1155.     if lastTime == nil then
  1156.         lastTime = 0
  1157.     end
  1158.    
  1159.     --checking if missing
  1160.     if KiloTicksPassed(lastTime, curTime) > sdata.settings.statusUpdate then
  1161.         --sending status request
  1162.         PrintDbg("DrawWidgetReferencePoint(): status req", 1)
  1163.         SendPosRequest(controllerId)
  1164.     end
  1165.     if KiloTicksPassed(lastTime, curTime) > sdata.settings.statusTimeout then
  1166.         --marking as missing
  1167.         missing = true
  1168.     end
  1169.    
  1170.     if missing then
  1171.         monitor.setBackgroundColor(colors.orange)
  1172.     else
  1173.         monitor.setBackgroundColor(sdata.settings.guiBgColor)
  1174.     end
  1175.    
  1176.     if T_ctrlTempData[controllerId].pos == nil then
  1177.         PrintDbg("DrawWidgetReferencePoint() 'pos' result missing", 1)
  1178.         T_ctrlTempData[controllerId].pos = {}
  1179.         SendPosRequest(controllerId)
  1180.     end
  1181.    
  1182.     local widgetText = ""
  1183.    
  1184.     if T_ctrlTempData[controllerId].pos[1] == nil or T_ctrlTempData[controllerId].pos[2] == nil or T_ctrlTempData[controllerId].pos[3] == nil then
  1185.         PrintDbg("DrawWidgetReferencePoint() 'pos' result is nil", 1)
  1186.         SendPosRequest(controllerId)
  1187.         return
  1188.     end
  1189.    
  1190.     if T_guiTempData[guiDataId] == nil then
  1191.         T_guiTempData[guiDataId] = {}
  1192.     end
  1193.    
  1194.     if T_guiTempData[guiDataId].origin == nil then
  1195.         T_guiTempData[guiDataId].origin = {}
  1196.     end
  1197.    
  1198.     T_guiTempData[guiDataId].origin.x = tonumber(T_ctrlTempData[controllerId].pos[1]) - lx
  1199.     T_guiTempData[guiDataId].origin.y = tonumber(T_ctrlTempData[controllerId].pos[2]) - ly
  1200.     T_guiTempData[guiDataId].origin.z = tonumber(T_ctrlTempData[controllerId].pos[3]) - lz
  1201.    
  1202.     if guiMode == "MODE_VERSION" then
  1203.         widgetText = tostring(T_ctrlTempData[controllerId].version)
  1204.     elseif guiMode == "MODE_OVERRIDE" then
  1205.         widgetText = tostring(T_ctrlTempData[controllerId].override)
  1206.     elseif guiMode == "MODE_GFOR" then
  1207.         widgetText = tostring(T_guiTempData[guiDataId].origin.x)..";"..tostring(T_guiTempData[guiDataId].origin.y)..";"..tostring(T_guiTempData[guiDataId].origin.z)
  1208.    
  1209.         if widget.draw.len < string.len(widgetText) then
  1210.             widgetText = string.sub(tostring(T_guiTempData[guiDataId].origin.x),-3)..";"..string.sub(tostring(T_guiTempData[guiDataId].origin.y),-3)..";"..string.sub(tostring(T_guiTempData[guiDataId].origin.z),-3)
  1211.         end
  1212.     elseif guiMode == "MODE_LFOR" then
  1213.        
  1214.         widgetText = tostring(lx)..";"..tostring(ly)..";"..tostring(lz)
  1215.    
  1216.         if widget.draw.len < string.len(widgetText) then
  1217.             widgetText = string.sub(tostring(lx),-3)..";"..string.sub(tostring(ly),-3)..";"..string.sub(tostring(lz),-3)
  1218.         end
  1219.     end
  1220.    
  1221.     for i = xPosition, xPosition + widget.draw.len do
  1222.         local letterPos = i + 1 - xPosition
  1223.         if letterPos <= string.len(widgetText) then
  1224.             monitor.write(string.sub(widgetText, letterPos, letterPos))
  1225.         else
  1226.             monitor.write(" ")
  1227.         end
  1228.        
  1229.         if T_guiXy[i] == nil then
  1230.             T_guiXy[i] = {}
  1231.         end
  1232.         T_guiXy[i][yPosition] = guiDataId
  1233.     end
  1234. end
  1235.  
  1236.  
  1237. function DrawWidgetEngageButton(guiDataId)
  1238.     local widget = sdata.guiData[guiDataId]
  1239.     if widget.draw.len == nil or widget.draw.yPos == nil or widget.guiName == nil then
  1240.         PrintDbg("DrawWidgetEngageButton() data missing", 2)
  1241.         return
  1242.     end
  1243.     xPosition = 1
  1244.     if widget.draw.xPos == nil then
  1245.         xPosition = mSizeX - widget.draw.len
  1246.     else
  1247.         xPosition = widget.draw.xPos
  1248.     end
  1249.     monitor.setCursorPos(xPosition, widget.draw.yPos)
  1250.     monitor.setTextColor(sdata.settings.guiBgColor)
  1251.     monitor.setBackgroundColor(colors.red)
  1252.    
  1253.     for i = xPosition, xPosition + widget.draw.len do
  1254.         local letterPos = i + 1 - xPosition
  1255.         monitor.setCursorPos(i, widget.draw.yPos)
  1256.         monitor.write(" ")
  1257.         monitor.setCursorPos(i, widget.draw.yPos+1)
  1258.         if letterPos <= string.len(widget.guiName) then
  1259.             monitor.write(string.sub(widget.guiName, letterPos, letterPos))
  1260.         else
  1261.             monitor.write(" ")
  1262.         end
  1263.         monitor.setCursorPos(i, widget.draw.yPos+2)
  1264.         monitor.write(" ")
  1265.        
  1266.         if T_guiXy[i] == nil then
  1267.             T_guiXy[i] = {}
  1268.         end
  1269.         T_guiXy[i][widget.draw.yPos] = guiDataId
  1270.         T_guiXy[i][widget.draw.yPos+1] = guiDataId
  1271.         T_guiXy[i][widget.draw.yPos+2] = guiDataId
  1272.     end
  1273. end
  1274.  
  1275.  
  1276. ---------------------------------------------------------------------------------------------------
  1277. -------------------         WIDGET CLICKING
  1278. ---------------------------------------------------------------------------------------------------
  1279.  
  1280. function ProcessClick(cx, cy)
  1281.     PrintDbg("Click at "..tostring(cx).." "..tostring(cy), 2)
  1282.     if T_guiXy[cx] == nil then
  1283.         PrintDbg("No controls an x = "..cx, 2)
  1284.         os.queueEvent("redraw")
  1285.         return
  1286.     else
  1287.         if T_guiXy[cx][cy] == nil then
  1288.             PrintDbg("No controls an x = "..cx.." y = "..cy, 2)
  1289.             os.queueEvent("redraw")
  1290.             return
  1291.         else
  1292.             --success
  1293.             local guiDataId = T_guiXy[cx][cy]
  1294.             if sdata.guiData[guiDataId] == nil then
  1295.                 PrintDbg("GUI ID "..guiDataId.." not found", 2)
  1296.             else
  1297.                 local guiType = sdata.guiData[guiDataId].guiType
  1298.                 if guiType == "SWITCH" then
  1299.                     ClickWidgetSwitch(guiDataId)
  1300.                 elseif guiType == "LABEL" or guiType == "SENSOR" or guiType == "ENER_BARS" then
  1301.                     DrawAll()
  1302.                 elseif guiType == "LASER_EM" then
  1303.                     ClickWidgetLaserEm(guiDataId)
  1304.                 elseif guiType == "GUI_MODE_SWITCH" then
  1305.                     ClickWidgetGuiModeSwitch(guiDataId)
  1306.                 elseif guiType == "TARGET_TABLE" then
  1307.                     ClickWidgetTargetTable(guiDataId, cx, cy)
  1308.                 elseif guiType == "REFERENCE_POINT" then
  1309.                     ClickWidgetReferencePoint(guiDataId)
  1310.                 elseif guiType == "ENGAGE_BUTTON" then
  1311.                     ClickWidgetEngageButton(guiDataId)
  1312.                 else
  1313.                     PrintDbg("GUI ID "..guiDataId..": unknown type", 2)
  1314.                 end
  1315.             end
  1316.         end
  1317.     end
  1318. end
  1319.  
  1320.  
  1321. --for simple on/off controllers, which state is depicted by color string, only first controllerId is used
  1322. function ClickWidgetSwitch(guiDataId)
  1323.     local controller_id = sdata.guiData[guiDataId].controllerIds[1]
  1324.     if T_ctrlTempData[controller_id] == nil then
  1325.         PrintDbg("No temp data for controller_id "..controller_id, 2)
  1326.         return
  1327.     end
  1328.    
  1329.     local packetT =
  1330.     {
  1331.         target = controller_id
  1332.     }
  1333.    
  1334.     if guiMode == "MODE_UPDATE" then
  1335.         packetT.command = "UPDATE"
  1336.     elseif guiMode == "MODE_OVERRIDE" then
  1337.         if T_ctrlTempData[controller_id].override == false then
  1338.             packetT.command = "OVERRIDE"       
  1339.         else
  1340.             packetT.command = "OVERRIDE OFF"
  1341.         end
  1342.     else
  1343.         packetT.command = "SET STATE"
  1344.         if T_ctrlTempData[controller_id].state == colors.red then
  1345.             packetT.state = colors.green
  1346.         else
  1347.             packetT.state = colors.red
  1348.         end
  1349.         PrintDbg("sending SET STATE to "..controller_id, 2)
  1350.     end
  1351.        
  1352.     local packet = textutils.serialize(packetT)
  1353.     modem.transmit(sdata.settings.channelSend, sdata.settings.channelReceive, packet)
  1354. end
  1355.  
  1356.  
  1357. --for simple on/off controllers, which state is depicted by color string, only first controllerId is used
  1358. function ClickControllerAirgenAll(guiDataId)
  1359.     local controller_id = sdata.guiData[guiDataId].controllerIds[1]
  1360.     if T_ctrlTempData[controller_id] == nil then
  1361.         PrintDbg("No temp data for controller_id "..controller_id, 2)
  1362.         return
  1363.     end
  1364.    
  1365.     local packetT =
  1366.     {
  1367.         target = "AIRGEN",
  1368.         command = "SET STATE",
  1369.         state = colors.green
  1370.     }
  1371.    
  1372.     PrintDbg("sending SET STATE GREEN to all AIRGENS", 2)
  1373.     local packet = textutils.serialize(packetT)
  1374.     modem.transmit(sdata.settings.channelSend, sdata.settings.channelReceive, packet)
  1375. end
  1376.  
  1377.  
  1378. function ClickWidgetGuiModeSwitch(guiDataId)
  1379.     if guiMode ~= sdata.guiData[guiDataId].guiMode then
  1380.         guiMode = sdata.guiData[guiDataId].guiMode
  1381.         os.queueEvent("redraw")
  1382.     else
  1383.         guiMode = "MODE_DEFAULT"
  1384.         os.queueEvent("redraw")
  1385.     end
  1386. end
  1387.  
  1388.  
  1389. function ClickWidgetLaserEm(guiDataId)
  1390.     local controller_id = sdata.guiData[guiDataId].controllerIds[1]
  1391.     if T_ctrlTempData[controller_id] == nil then
  1392.         PrintDbg("No temp data for controller_id "..controller_id, 2)
  1393.         return
  1394.     end
  1395.    
  1396.     --TODO toggle state
  1397.     local packetT =
  1398.     {
  1399.         target = controller_id
  1400.     }
  1401.    
  1402.     if guiMode == "MODE_UPDATE" then
  1403.         packetT.command = "UPDATE"
  1404.     elseif guiMode == "MODE_OVERRIDE" then
  1405.         if T_ctrlTempData[controller_id].override == false then
  1406.             packetT.command = "OVERRIDE"       
  1407.         else
  1408.             packetT.command = "OVERRIDE OFF"
  1409.         end
  1410.     else
  1411.         packetT.command = "SET STATE"
  1412.         if T_ctrlTempData[controller_id].state == colors.red then
  1413.             packetT.state = colors.green
  1414.         else
  1415.             packetT.state = colors.red
  1416.         end
  1417.         PrintDbg("sending SET STATE to "..controller_id, 2)
  1418.     end
  1419.        
  1420.     local packet = textutils.serialize(packetT)
  1421.     modem.transmit(sdata.settings.channelSend, sdata.settings.channelReceive, packet)
  1422. end
  1423.  
  1424.  
  1425. function ClickWidgetTargetTable(guiDataId, cX, cY)
  1426.     if T_guiTempData[guiDataId] == nil then
  1427.         PrintDbg("No temp data for controller_id "..controller_id, 2)
  1428.         return
  1429.     end
  1430.     local length = table.getn(T_guiTempData[guiDataId].targetList)
  1431.     local widget = sdata.guiData[guiDataId]
  1432.     local yPosition = tonumber(widget.draw.yPos)
  1433.     local xPosition = tonumber(widget.draw.xPos)
  1434.     if cY == mSizeY then
  1435.         --buttons
  1436.         if cX >= xPosition and cX < xPosition + 5 then
  1437.             --add
  1438.             print("Coordinates: x;y;z;comment")
  1439.             local inputString = tostring(read())
  1440.             local cmt = ""
  1441.             local smcln = string.find(inputString, ";", 1, true)
  1442.             if smcln == nil then
  1443.                 print("failed!")
  1444.                 return
  1445.             end
  1446.             local tX = tonumber(string.sub(inputString,1,smcln-1))
  1447.             inputString = string.sub(inputString,smcln+1)
  1448.             smcln = string.find(inputString.."", ";", 1, true)
  1449.             if smcln == nil then
  1450.                 print("failed!")
  1451.                 return
  1452.             end
  1453.             local tY = tonumber(string.sub(inputString,1,smcln-1))
  1454.             inputString = string.sub(inputString,smcln+1)
  1455.             smcln = string.find(inputString.."", ";", 1, true)
  1456.             local tZ = 0;
  1457.             if smcln == nil then
  1458.                 tZ = tonumber(inputString)
  1459.             else
  1460.                 tZ = tonumber(string.sub(inputString,1,smcln-1))
  1461.                 cmt = string.sub(inputString,smcln+1)              
  1462.             end
  1463.             table.insert(T_guiTempData[guiDataId].targetList, {x = tX, y = tY, z = tZ, comment = cmt})
  1464.            
  1465.             os.queueEvent("redraw")
  1466.             return
  1467.            
  1468.         elseif cX == xPosition + 6 then
  1469.             --minus
  1470.             if T_guiTempData[guiDataId].selected ~= nil then
  1471.                 table.remove(T_guiTempData[guiDataId].targetList, tonumber(T_guiTempData[guiDataId].selected))
  1472.             end
  1473.            
  1474.             os.queueEvent("redraw")
  1475.             return
  1476.            
  1477.         elseif cX >= xPosition + 8 and cX < xPosition + 14 then
  1478.             --TODO save to file
  1479.             WriteResume(T_guiTempData[guiDataId].targetList, "ldata")
  1480.            
  1481.             return
  1482.         end
  1483.     else
  1484.         T_guiTempData[guiDataId].selected = tonumber(length - cY + yPosition)
  1485.         if T_guiTempData[guiDataId].selected < 1 then
  1486.             T_guiTempData[guiDataId].selected = nil
  1487.         end
  1488.        
  1489.         os.queueEvent("redraw")
  1490.     end
  1491.    
  1492.    
  1493. end
  1494.  
  1495.  
  1496. --tries to update its global coordinates
  1497. function ClickWidgetReferencePoint(guiDataId)
  1498.     local controller_id = sdata.guiData[guiDataId].controllerIds[1]
  1499.     if T_ctrlTempData[controller_id] == nil then
  1500.         PrintDbg("No temp data for controller_id "..controller_id, 2)
  1501.         return
  1502.     end
  1503.    
  1504.     local packetT =
  1505.     {
  1506.         target = controller_id
  1507.     }
  1508.    
  1509.     if guiMode == "MODE_UPDATE" then
  1510.         packetT.command = "UPDATE"
  1511.         local packet = textutils.serialize(packetT)
  1512.         modem.transmit(sdata.settings.channelSend, sdata.settings.channelReceive, packet)
  1513.     elseif guiMode == "MODE_OVERRIDE" then
  1514.         if T_ctrlTempData[controller_id].override == false then
  1515.             packetT.command = "OVERRIDE"       
  1516.         else
  1517.             packetT.command = "OVERRIDE OFF"
  1518.         end
  1519.         local packet = textutils.serialize(packetT)
  1520.         modem.transmit(sdata.settings.channelSend, sdata.settings.channelReceive, packet)
  1521.     else
  1522.         SendPosRequest(controller_id)
  1523.     end
  1524. end
  1525.  
  1526. function ClickWidgetEngageButton(guiDataId)
  1527.     if T_guiTempData[guiDataId] ~= nil and modem ~= nil then
  1528.         for key,value in pairs( T_guiTempData[guiDataId] ) do
  1529.             PrintDbg("ClickWidgetEngageButton(): sending to "..tostring(key), 2)
  1530.             modem.transmit(sdata.settings.channelSend, sdata.settings.channelReceive, value)
  1531.         end
  1532.         T_guiTempData[guiDataId] = nil
  1533.         os.queueEvent("redraw")
  1534.     elseif T_guiTempData[guiDataId] == nil then
  1535.         PrintDbg("ClickWidgetEngageButton(): guiDataId is null", 0)
  1536.     end
  1537.     PrintDbg("ClickWidgetEngageButton(): done", 0)
  1538. end
  1539.  
  1540.  
  1541.  
  1542. --MAIN BODY should look like this:
  1543.  
  1544. Init()
  1545.  
  1546. while true do
  1547.     parallel.waitForAny(GuiLoop, ReceiveLoop, RedrawLoop)
  1548. end
  1549. --MAIN BODY END
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement