Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --
- -- Master control and monitor turbines of a active cooled
- -- Big Reactor (http://big-reactors.com/).
- --
- -- Author: kla_sch
- --
- -- History:
- -- v0.1, 2014-12-29:
- -- - first version
- --
- -- v0.2, 2015-01-02
- -- - smaller monitors (25 characters) and portable computers supported.
- -- - change table layout to fit 50 characters per line.
- -- - minor bugfixes
- -- - code improvments (thanks to wieselkatze)
- --
- -- v0.3, 2015-01-05
- -- - now support OpenPeripheral Terminal Glasses
- -- - minor bugfixes
- --
- -- Save as "startup"
- --
- -- Constant values (configuration)
- --
- -- Loop time
- local loopT=1
- -- Modem channel to send status requests to any turbine.
- local stateRequestChannel = 32768 -- Broadcast channel.
- -- Position of display on terminal glasses.
- -- The position 0,0 (posX, posY) is the upper left corner of the display.
- -- There are also some other basic style values.
- local tGlassStyle = {
- posX = 5, -- Horizontal position from the left side
- posY = 5, -- Vertical position from top
- lineHeight = 8, -- Height of line
- rulerHeight = 2, -- Height of ruler
- borderWidth = 2, -- Width of box border
- padding = 2, -- Padding between box and text
- spacing = 2 -- Spacing between lines
- }
- --
- -- Internal values:
- --
- local turbineTable = {} -- state of turbines
- local labelArr = {} -- sorted array of turbine computer labels
- local glassesStates = nil -- table to hold state of the terminal glasses
- -- Cycle flag for small monitors (width of 25 chars)
- local cycleDisplayStates = {}
- --
- -- Send modem request to any attached modem
- --
- local function sendBroadcastRequest()
- local pList = peripheral.getNames()
- local i, name
- for i, name in pairs(pList) do
- if peripheral.getType(name) == "modem" then
- peripheral.call(name, "open", os.getComputerID())
- peripheral.call(name, "transmit",
- stateRequestChannel, os.getComputerID(),
- "BR_turbine_get_state")
- end
- end
- end
- --
- -- Send turbine speed change request to any attached modem
- --
- -- Parameters:
- -- computerId - Id of turbine controller
- -- speed - new speed
- --
- local function sendSpeedChange(computerId, speed)
- local pList = peripheral.getNames()
- local i, name
- for i, name in pairs(pList) do
- if peripheral.getType(name) == "modem" then
- peripheral.call(name, "open", os.getComputerID())
- peripheral.call(name, "transmit",
- computerId, os.getComputerID(),
- "BR_turbine_set_speed:" .. speed)
- end
- end
- end
- --
- -- Format integer value, align right. (repace buggy string.format)
- --
- -- Parameters:
- -- number - The output value
- -- width - Output width
- --
- -- Return:
- -- Formated string
- --
- local function format_int(number, width)
- local result = tostring(math.floor(number + 0.5))
- if string.len(result) < width then
- result = string.rep(" ", width - string.len(result)) .. result
- end
- return result
- end
- --
- -- Format string value, align left, cut off. (repace buggy string.format)
- --
- -- Parameters:
- -- str - the output value
- -- width - output width
- --
- -- Return:
- -- formated string
- --
- local function format_str(str, width)
- if string.len(str) < width then -- fill
- result = str .. string.rep(" ", width - string.len(str))
- elseif string.len(str) > width then -- cut
- result = string.sub(str, 1, width)
- else
- result = str
- end
- return result
- end
- --
- -- Glasses: Format string value, align left, cutt off.
- --
- -- This function does not work well, if you use a buggy version of
- -- Open Peripherals. Older versions returns the wrong width of the string.
- --
- -- Parameters:
- -- glasses - Wraped glasses bridge
- -- str - the output value
- -- width - maximum output width (pixed)
- --
- -- Returns: A string that fits
- --
- local function format_gl_str(glasses, str, width)
- local strLen = glasses.getStringWidth(str)
- if strLen <= width then
- return str -- It fits
- end
- -- Too long: Test differnet lengths
- local minLen = 1
- local maxLen = string.len(str)
- while maxLen - minLen > 1 do
- local testLen = minLen + math.floor( (maxLen - minLen) / 2)
- if glasses.getStringWidth(string.sub(str, 1, testLen)) > width then
- maxLen = testLen
- else
- minLen = testLen
- end
- end
- return string.sub(str, 1, minLen)
- end
- --
- -- Format float value, align right. (repace buggy string.format)
- --
- -- Parameters:
- -- number - the output value
- -- width - output width
- -- precision - precision of number
- --
- -- Return:
- -- formated string
- --
- local function format_float(number, width, precision)
- local pExp = 10 ^ precision
- local result = tostring(math.floor(number * pExp + 0.5) / pExp)
- local s,e = string.find(result, ".", 1, true)
- if s == nil then
- result = result .. "." .. string.rep("0", precision)
- else
- local pLen = string.len(result) - s
- if pLen < precision then -- fill with '0'
- result = result .. string.rep("0", precision - pLen)
- end
- end
- if string.len(result) < width then -- fill with ' '
- result = string.rep(" ", width - string.len(result)) .. result
- end
- return result
- end
- --
- -- Write text with colors, if possible (advance monitor)
- --
- -- Parameters:
- -- mon - handle of monitor
- -- color - text color
- -- text - text to write
- --
- local function writeColor(mon, color, text)
- if mon.isColor() then
- mon.setTextColor(color)
- end
- mon.write(text)
- if mon.isColor() then
- mon.setTextColor(colors.white)
- end
- end
- --
- -- Scale the monitor text size to needed size of output text.
- --
- -- This function try to scale the monitor text size, so that it is enoth for
- -- "optHeight" lines with "optWidth" characters. If it is not possible
- -- (text scale is not supported or the connected monitor is too small),
- -- it also accept "minHeight" lines and "minWidth" characters.
- --
- -- Parameters:
- -- mon - handle of monitor.
- -- minWidth - Minimum number of columns needed.
- -- optWidth - Optimal number of columns desired.
- -- minHeight - Minimum number of rows needed.
- -- optHeight - Optimal number of rows desired.
- --
- -- Return:
- -- Size of monitor after scaling: width, height.
- -- If the monitor is too small, it returns nul,nil.
- --
- local function scaleMonitor(mon, minWidth, optWidth, minHeight, optHeight)
- if mon.setTextScale ~= nil then
- mon.setTextScale(1)
- end
- local width, height = mon.getSize()
- if mon.setTextScale == nil then
- -- Scale not available
- if width < minWidth or height < minHeight then
- return nil, nil -- too small
- else
- return width, height
- end
- end
- if width < optWidth or height < optHeight then
- -- too small: try to scale down.
- mon.setTextScale(0.5)
- width, height = mon.getSize()
- if width < minWidth or height < minHeight then
- return nil, nil -- still too small
- end
- else
- -- Huge monitors? Try to scale up, if possible (max scale=5).
- local scale = math.min(width / optWidth, height / optHeight, 5)
- scale = math.floor(scale * 2) / 2 -- multiple of 0.5
- if scale > 1 then
- mon.setTextScale(scale)
- width, height = mon.getSize()
- end
- end
- return width, height
- end
- --
- -- Display turbine data to monitor
- --
- -- Parameters:
- -- mon - Name of monitor. "#term" for computer terminal.
- --
- local function displayTableOnMonitor(monName)
- local mon
- if monName == "#term" then
- mon = term
- else
- mon = peripheral.wrap(monName)
- end
- mon.clear()
- local width, height = scaleMonitor(mon, 25, 50, 5, 5)
- if width == nil or height == nil then
- return -- Montitor is too small
- end
- local cycleState = nil
- if width < 50 then
- -- small monitor
- height = height - 1 -- one line for information
- cycleState = cycleDisplayStates[monName]
- if cycleState == nil then
- -- initialize cycle state
- cycleDisplayStates[monName] = "RPM"
- end
- if cycleState == "RPM" then
- mon.setCursorPos(1,1)
- mon.write("Label TRPM ARPM")
- mon.setCursorPos(1,2)
- mon.write("============== ==== ====")
- elseif cycleState == "Flow" then
- mon.setCursorPos(1,1)
- mon.write("Label MFlow AFlow")
- mon.setCursorPos(1,2)
- mon.write("============== ===== =====")
- elseif cycleState == "EnergyRate" then
- mon.setCursorPos(1,1)
- mon.write("Label kRF/t")
- mon.setCursorPos(1,2)
- mon.write("============== =====")
- elseif cycleState == "EnergyStored" then
- mon.setCursorPos(1,1)
- mon.write("Label Energy")
- mon.setCursorPos(1,2)
- mon.write("============== =======")
- end
- mon.setCursorPos(1, height + 1)
- if pocket then
- mon.write("* Press space to cycle")
- else
- mon.write("* Click here to cycle")
- end
- else
- cycleDisplayStates[monName] = nil -- full sized monitor
- mon.setCursorPos(1,1)
- mon.write("Label TRPM ARPM MFlow AFlow kRF/t Energy ")
- mon.setCursorPos(1,2)
- mon.write("============== ==== ==== ===== ===== ===== =======")
- end
- local i, label, tState
- labelArr = {}
- for label, tState in pairs(turbineTable) do
- table.insert(labelArr, label)
- end
- table.sort(labelArr)
- local line = 3
- for i, label in pairs(labelArr) do
- tState = turbineTable[label]
- if line <= height then
- mon.setCursorPos(1, line)
- -- prefix
- if tState.active then
- label = "+" .. label
- writeColor(mon, colors.green, format_str(label, 14))
- else
- label = "-" .. label
- writeColor(mon, colors.red, format_str(label, 14))
- end
- if cycleState == nil or cycleState == "RPM" then
- -- target speed
- if tState.tSpeed == 0 then
- writeColor(mon, colors.red, " OFF")
- else
- mon.write(" "..format_int(tState.tSpeed, 4))
- end
- -- actual speed
- if math.floor(tState.rotorSpeed*10 + 0.5)/10 == tState.tSpeed then
- writeColor(mon, colors.green,
- " " ..format_int(tState.rotorSpeed, 4))
- elseif tState.rotorSpeed > 2000 then
- writeColor(mon, colors.red,
- " " ..format_int(tState.rotorSpeed, 4))
- else
- writeColor(mon, colors.orange,
- " " ..format_int(tState.rotorSpeed, 4))
- end
- end
- if cycleState == nil or cycleState == "Flow" then
- -- steam flow
- mon.write(" "..format_int(tState.fluidFlowRateMax, 5))
- if tState.fluidFlowRate == tState.fluidFlowRateMax then
- writeColor(mon, colors.green,
- " "..format_int(tState.fluidFlowRate, 5))
- else
- writeColor(mon, colors.orange,
- " "..format_int(tState.fluidFlowRate, 5))
- end
- end
- if cycleState == nil or cycleState == "EnergyRate" then
- -- energy rate
- mon.write(" "..format_float(tState.energyProducedLastTick / 1000,
- 5, 2))
- end
- if cycleState == nil or cycleState == "EnergyStored" then
- -- energy stored
- mon.write(" "..format_int(tState.energyStored, 7))
- end
- line = line + 1
- end
- end
- end
- --
- -- Displays a bordered box
- --
- -- Parameters:
- -- glasses - wraped glasses bridge
- -- width -- outer width of the box
- -- height -- inner width of the box
- --
- local function displayBoxToGlasses(glasses, width, height)
- glasses.addBox(tGlassStyle.posX, tGlassStyle.posY,
- width, height, 0xFFFF00, 0.5)
- width = width - 2 * tGlassStyle.borderWidth
- height = height - 2 * tGlassStyle.borderWidth
- glasses.addBox(tGlassStyle.posX + tGlassStyle.borderWidth,
- tGlassStyle.posY + tGlassStyle.borderWidth,
- width, height, 0x0000FF, 0.5)
- end
- --
- -- Display a help box on terminal glasses
- --
- -- Parameters:
- -- glasses - wraped glasses bridge
- --
- local function displayHelpToGlasses(glasses)
- glasses.clear()
- local boxHeight =
- 2 * tGlassStyle.borderWidth -- two borders
- + 2 * tGlassStyle.padding -- two paddings
- + 4 * tGlassStyle.lineHeight -- toppic
- + 9 * (tGlassStyle.lineHeight + tGlassStyle.padding) -- 8 lines
- displayBoxToGlasses(glasses, 300, boxHeight)
- local textX =
- tGlassStyle.posX + tGlassStyle.borderWidth + tGlassStyle.padding
- local textY =
- tGlassStyle.posY + tGlassStyle.borderWidth + tGlassStyle.padding
- local tabStopWidth = 120
- local header =
- glasses.addText(textX, textY, "Terminal Glasses Commands")
- header.setScale(2)
- textY = textY + 4 * tGlassStyle.lineHeight
- glasses.addText(textX, textY, "$$help")
- glasses.addText(textX + tabStopWidth, textY,
- "open this help screen")
- textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
- glasses.addText(textX, textY, "$$hide")
- glasses.addText(textX + tabStopWidth, textY,
- "hide all screens")
- textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
- glasses.addText(textX, textY, "$$show")
- glasses.addText(textX + tabStopWidth, textY,
- "show table with turbines")
- textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
- glasses.addText(textX, textY, "$$show <name>")
- glasses.addText(textX + tabStopWidth, textY,
- "show turbine data of <name>")
- textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
- glasses.addText(textX, textY, "$$speed off <name>")
- glasses.addText(textX + tabStopWidth, textY,
- "set turbine <name> to off")
- textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
- glasses.addText(textX, textY, "$$speed 900 <name>")
- glasses.addText(textX + tabStopWidth, textY,
- "set turbine <name> to 900 RPM")
- textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
- glasses.addText(textX, textY, "$$speed 1800 <name>")
- glasses.addText(textX + tabStopWidth, textY,
- "set turbine <name> to 1800 RPM")
- textY = textY + 2 * (tGlassStyle.lineHeight + tGlassStyle.spacing)
- glasses.addText(textX, textY, "Exit this help with $$show or $$hide")
- end
- --
- -- Display error message on terminal glasses
- --
- -- Parameters:
- -- glasses - wraped glasses bridge
- -- msgId - identifier of error
- --
- local function displayErrorMsgToGlasses(glasses, msgId)
- glasses.clear()
- local boxHeight =
- 2 * tGlassStyle.borderWidth -- two borders
- + 2 * tGlassStyle.padding -- two paddings
- + 4 * tGlassStyle.lineHeight -- toppic
- + 3 * (tGlassStyle.lineHeight + tGlassStyle.padding) -- 3 lines
- displayBoxToGlasses(glasses, 280, boxHeight)
- local textX =
- tGlassStyle.posX + tGlassStyle.borderWidth + tGlassStyle.padding
- local textY =
- tGlassStyle.posY + tGlassStyle.borderWidth + tGlassStyle.padding
- local header =
- glasses.addText(textX, textY, "Command Error")
- header.setScale(2)
- textY = textY + 4 * tGlassStyle.lineHeight
- local msgText
- if msgId == "turbine_not_found" then
- msgText = "The turbine you entered was not found in table"
- elseif msgId == "remote_control_disabled" then
- msgText = "Remote control feature disabled at turbine"
- elseif msgId == "speed_missing" then
- msgText = "New target speed is missing"
- elseif msgId == "invalid_turbine_speed" then
- msgText = "Invalid turbine speed value"
- end
- glasses.addText(textX, textY, msgText, 0xF2B233)
- textY = textY + 2 * tGlassStyle.lineHeight
- glasses.addText(textX, textY, "Type in $$help for help or try again")
- end
- --
- -- Display single information line to terminal glasses
- --
- -- Parameters:
- -- glasses - wraped handler of terminal glasses bridge
- -- x - X-Position of label
- -- y - Y-Position of line
- -- lable - Label of information line
- -- value - value of information line
- -- color - color for value
- -- update - flag: only update information
- --
- local function displayInfoLineOnGlasses(glasses, x, y, label, value,
- color, update)
- local tabStopWidth = 100
- if update then
- glassesStates.infoTxt[label].setText(value)
- else
- glasses.addText(x, y, label)
- glassesStates.infoTxt[label] =
- glasses.addText(x + tabStopWidth, y, value)
- end
- glassesStates.infoTxt[label].setColor(color)
- end
- --
- -- Display any known informations of a single turbine to terminal glasses
- --
- -- Parameters:
- -- glasses - wraped handler of terminal glasses bridge
- --
- local function displayInfoOnGlasses(glasses)
- if glassesStates.infoTurbine == nil then
- glassesStates.show = "table"
- return -- no turbine name? ups!
- end
- local tState = turbineTable[glassesStates.infoTurbine]
- local noInfo = "<no information>"
- local textX =
- tGlassStyle.posX + tGlassStyle.borderWidth + tGlassStyle.padding
- local textY =
- tGlassStyle.posY + tGlassStyle.borderWidth + tGlassStyle.padding
- local updateOnly = true
- if glassesStates.infoTxt == nil then
- -- need a new box
- updateOnly = false
- glassesStates.infoTxt = {}
- glasses.clear()
- local boxHeight =
- 2 * tGlassStyle.borderWidth -- two borders
- + 2 * tGlassStyle.padding -- two paddings
- + 4 * tGlassStyle.lineHeight -- toppic
- + 13 * (tGlassStyle.lineHeight + tGlassStyle.padding) -- 13 lines
- displayBoxToGlasses(glasses, 240, boxHeight)
- local header =
- glasses.addText(textX, textY, "Turbine information")
- header.setScale(2)
- textY = textY + 4 * tGlassStyle.lineHeight
- end
- local outStr, color
- -- Label
- outStr = glassesStates.infoTurbine
- if tState ~= nil and tState.active then
- color=0x00FF00 -- green
- else
- color=0xFF0000 -- red
- end
- displayInfoLineOnGlasses(glasses, textX, textY,
- "Label", outStr, color, updateOnly)
- textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
- -- Active state
- if tState ~= nil and tState.active then
- outStr = "Activated"
- color = 0x00FF00
- elseif tState == nil then
- outStr = "Turbine not reachable"
- color = 0xFF0000
- else
- outStr = "Deactivated"
- color = 0xFF0000
- end
- displayInfoLineOnGlasses(glasses, textX, textY,
- "State", outStr, color, updateOnly)
- textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
- -- Remote Control
- if tState then
- if tState.remoteControl then
- outStr = "Activated"
- color = 0x00FF00 -- green
- else
- outStr = "Deactivated"
- color = 0xFF0000 -- red
- end
- else
- outStr = noInfo
- color = 0xFF0000 -- red
- end
- displayInfoLineOnGlasses(glasses, textX, textY,
- "Remote Control", outStr, color, updateOnly)
- textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
- -- Target Speed
- if tState then
- outStr = tostring(math.floor(tState.tSpeed + 0.5)) .. " RPM"
- color = 0xFFFFFF -- white
- else
- outStr = noInfo
- color = 0xFF0000 -- red
- end
- displayInfoLineOnGlasses(glasses, textX, textY,
- "Target Speed", outStr, color, updateOnly)
- textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
- -- Actual Speed
- if tState then
- local rotorSpeed = math.floor(tState.rotorSpeed*10+ 0.5) /10
- outStr = tostring(rotorSpeed) .. " RPM"
- if rotorSpeed == tState.tSpeed
- then
- color = 0x00FF00 -- green
- elseif rotorSpeed >= 2000 then
- color = 0xFF0000 -- red
- else
- color = 0xF2B233 -- orange
- end
- else
- outStr = noInfo
- color = 0xFF0000
- end
- displayInfoLineOnGlasses(glasses, textX, textY,
- "Actual Speed", outStr, color, updateOnly)
- textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
- -- Maximum Flow Rate
- if tState then
- local flowRateMax =
- math.floor(tState.fluidFlowRateMax * 10 + 0.5) / 10
- local flowRateMaxMax =
- math.floor(tState.fluidFlowRateMaxMax * 10 + 0.5) / 10
- outStr =
- tostring(flowRateMax) .. " mB/t"
- .." of " .. tostring(flowRateMaxMax) .. " mB/t"
- color = 0xFFFFFF -- white
- else
- outStr = noInfo
- color = 0xFF0000 -- red
- end
- displayInfoLineOnGlasses(glasses, textX, textY,
- "Max Flow Rate", outStr, color, updateOnly)
- textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
- -- Actual Flow Rate
- if tState then
- local fluidFlowRate = math.floor(tState.fluidFlowRate * 10 + 0.5) / 10
- outStr = tostring(fluidFlowRate) .. " mB/t"
- if tState.fluidFlowRateMax == tState.fluidFlowRate then
- color = 0x00FF00 -- green
- else
- color = 0xF2B233 -- orange
- end
- else
- outStr = noInfo
- color = 0xFF0000 -- red
- end
- displayInfoLineOnGlasses(glasses, textX, textY,
- "Act Flow Rate", outStr, color, updateOnly)
- textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
- -- Energy Output
- if tState then
- local energyOut = math.floor(tState.energyProducedLastTick + 0.5)
- outStr = tostring(energyOut) .. " RF/t"
- color = 0xFFFFFF -- white
- else
- outStr = noInfo
- color = 0xFF0000 -- red
- end
- displayInfoLineOnGlasses(glasses, textX, textY,
- "Energy Output", outStr, color, updateOnly)
- textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
- -- Energy Stored
- if tState then
- local energy = math.floor(tState.energyStored + 0.5)
- outStr = tostring(energy) .. " RF"
- color = 0xFFFFFF -- white
- else
- outStr = noInfo
- color = 0xFF0000 -- red
- end
- displayInfoLineOnGlasses(glasses, textX, textY,
- "Energy Stored", outStr, color, updateOnly)
- textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
- -- Intake Fluid
- if tState then
- local inputAmount = math.floor(tState.inputAmount * 10 + 0.5) / 10
- local inputType = "-"
- if tState.inputType then
- inputType = tState.inputType
- end
- outStr = tostring(inputAmount) .. " mB (" .. inputType .. ")"
- color = 0xFFFFFF -- white
- else
- outStr = noInfo
- color = 0xFF0000 -- red
- end
- displayInfoLineOnGlasses(glasses, textX, textY,
- "Intake Fluid", outStr, color, updateOnly)
- textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
- -- Exhaust Fluid
- if tState then
- local outputAmount = math.floor(tState.outputAmount * 10 + 0.5) / 10
- local outputType = "-"
- if tState.outputType then
- outputType = tState.outputType
- end
- outStr = tostring(outputAmount) .. " mB (" .. outputType .. ")"
- color = 0xFFFFFF -- white
- else
- outStr = noInfo
- color = 0xFF0000 -- red
- end
- displayInfoLineOnGlasses(glasses, textX, textY,
- "Exhaust Fluid", outStr, color, updateOnly)
- textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
- -- Max Fluid Amount
- if tState then
- local fluidAmount = math.floor(tState.fluidAmountMax * 10 + 0.5) / 10
- outStr = tostring(fluidAmount) .. " mB"
- color = 0xFFFFFF -- white
- else
- outStr = noInfo
- color = 0xFF0000 -- red
- end
- displayInfoLineOnGlasses(glasses, textX, textY,
- "Max Fluid Amount", outStr, color, updateOnly)
- textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
- -- Infuctor
- if tState then
- if tState.inductorEngaged == nil then
- outStr = "<not available>"
- color = 0xF2B233 -- orange
- elseif tState.inductorEngaged then
- outStr = "Engaged"
- color = 0x00FF00 -- green
- else
- outStr = "Disengaged"
- color = 0xF2B233 -- orange
- end
- else
- outStr = noInfo
- color = 0xFF0000 -- red
- end
- displayInfoLineOnGlasses(glasses, textX, textY,
- "Inductor", outStr, color, updateOnly)
- end
- --
- -- Display table on terminal glasses
- --
- -- Parameters:
- -- glasses - wraped terminal bridge device
- --
- local function displayTableOnGlasses(glasses)
- labelArr = {}
- local label, tState
- for label, tState in pairs(turbineTable) do
- table.insert(labelArr, label)
- end
- table.sort(labelArr)
- local updateOnly = (glassesStates.prevTableEntries == #labelArr)
- glassesStates.prevTableEntries = #labelArr
- if not updateOnly then
- -- redraw box
- local boxWidth =
- 51 * 8 -- Space enought for 52 characters
- + 2 * tGlassStyle.borderWidth -- two borders (top/bottom)
- + 2 * tGlassStyle.padding -- two padding (top/bottom)
- local boxHeight =
- tGlassStyle.lineHeight -- header line
- + tGlassStyle.spacing -- spacing of header line
- + tGlassStyle.rulerHeight + tGlassStyle.spacing -- ruler with spacing
- + (tGlassStyle.lineHeight + tGlassStyle.spacing)
- * #labelArr -- Table lines
- + 2 * tGlassStyle.borderWidth -- two borders (left/right)
- + 2 * tGlassStyle.padding -- two paddings (left/right)
- glasses.clear()
- displayBoxToGlasses(glasses, boxWidth, boxHeight)
- end
- -- Table Header
- local textX =
- tGlassStyle.posX + tGlassStyle.borderWidth + tGlassStyle.padding
- local textY =
- tGlassStyle.posY + tGlassStyle.borderWidth + tGlassStyle.padding
- if not updateOnly then
- glasses.addText(textX, textY, "Label")
- glasses.addText(textX + (8*16), textY, "TRPM")
- glasses.addText(textX + (8*21), textY, "ARPM")
- glasses.addText(textX + (8*26), textY, "MFlow")
- glasses.addText(textX + (8*32), textY, "AFlow")
- glasses.addText(textX + (8*38), textY, "kRF/t")
- glasses.addText(textX + (8*44), textY, "Energy")
- glasses.addBox(tGlassStyle.posX
- + tGlassStyle.borderWidth,
- tGlassStyle.posY
- + tGlassStyle.borderWidth
- + tGlassStyle.padding
- + tGlassStyle.lineHeight + tGlassStyle.spacing,
- 51 * 8 + 2 * tGlassStyle.padding, 2,
- 0xFFFFFF)
- end
- -- Table lines
- local i
- for i, label in pairs(labelArr) do
- tState = turbineTable[label]
- -- Label
- local linePos =
- tGlassStyle.posY -- Y-Pos
- + tGlassStyle.borderWidth -- the border
- + tGlassStyle.padding -- the padding
- + tGlassStyle.lineHeight -- header line
- + tGlassStyle.rulerHeight -- ruler
- + 2 * tGlassStyle.spacing -- 2 * spacings (header+ruler)
- + (i-1) * (tGlassStyle.lineHeight + tGlassStyle.spacing)
- if not glassesStates.tabTxt[i] then
- glassesStates.tabTxt[i] = {}
- end
- local outStr = format_gl_str(glasses, label, (15*8))
- if updateOnly then
- glassesStates.tabTxt[i].label.setText(outStr)
- else
- glassesStates.tabTxt[i].label =
- glasses.addText(textX, linePos, outStr)
- end
- if tState.active then
- glassesStates.tabTxt[i].label.setColor(0x00FF00)
- else
- glassesStates.tabTxt[i].label.setColor(0xFF0000)
- end
- -- TRPM
- if tState.tSpeed == 0 then
- outStr = "OFF"
- else
- outStr = tostring(math.floor(tState.tSpeed + 0.5))
- end
- if updateOnly then
- glassesStates.tabTxt[i].TRPM.setText(outStr)
- else
- glassesStates.tabTxt[i].TRPM =
- glasses.addText(textX + (8*16), linePos, outStr)
- end
- if tState.tSpeed == 0 then
- glassesStates.tabTxt[i].TRPM.setColor(0xFF0000) -- red
- else
- glassesStates.tabTxt[i].TRPM.setColor(0xFFFFFF) -- white
- end
- -- APRM
- outStr = tostring(math.floor(tState.rotorSpeed + 0.5))
- if updateOnly then
- glassesStates.tabTxt[i].ARPM.setText(outStr)
- else
- glassesStates.tabTxt[i].ARPM =
- glasses.addText(textX + (8*21), linePos, outStr)
- end
- if math.floor(tState.rotorSpeed*10 + 0.5)/10 == tState.tSpeed then
- glassesStates.tabTxt[i].ARPM.setColor(0x00FF00) -- green
- elseif tState.rotorSpeed > 2000 then
- glassesStates.tabTxt[i].ARPM.setColor(0xFF0000) -- red
- else
- glassesStates.tabTxt[i].ARPM.setColor(0xF2B233) -- orange
- end
- -- TFlow
- outStr = tostring(math.floor(tState.fluidFlowRateMax + 0.5))
- if updateOnly then
- glassesStates.tabTxt[i].TFlow.setText(outStr)
- else
- glassesStates.tabTxt[i].TFlow =
- glasses.addText(textX + (8*26), linePos, outStr)
- end
- -- AFlow
- outStr = tostring(math.floor(tState.fluidFlowRate + 0.5))
- if updateOnly then
- glassesStates.tabTxt[i].AFlow.setText(outStr)
- else
- glassesStates.tabTxt[i].AFlow =
- glasses.addText(textX + (8*32), linePos, outStr)
- end
- if tState.fluidFlowRate == tState.fluidFlowRateMax then
- glassesStates.tabTxt[i].AFlow.setColor(0x00FF00) -- green
- else
- glassesStates.tabTxt[i].AFlow.setColor(0xF2B233) -- orange
- end
- -- kRF/t
- local kRFt = tState.energyProducedLastTick / 1000
- outStr = tostring(math.floor(kRFt*100 + 0.5) / 100)
- if updateOnly then
- glassesStates.tabTxt[i].kRFt.setText(outStr)
- else
- glassesStates.tabTxt[i].kRFt =
- glasses.addText(textX + (8*38), linePos, outStr)
- end
- -- Energy
- outStr = tostring(math.floor(tState.energyStored + 0.5))
- if updateOnly then
- glassesStates.tabTxt[i].Energy.setText(outStr)
- else
- glassesStates.tabTxt[i].Energy =
- glasses.addText(textX + (8*44), linePos, outStr)
- end
- end
- end
- --
- -- Display table on terminal glasses
- --
- -- Parameters:
- -- name - name (side) of device
- --
- local function displayOnGlasses(name)
- local glasses = peripheral.wrap(name)
- if glassesStates == nil then
- -- initialize states of terminal glasses
- glasses.clear()
- glassesStates = {
- mode = "table", -- mode of display
- show = "hidden", -- what mode is currently shown
- recoverMode = "hidden", -- mode to recover after speed change error
- infoTurbine = nil, -- name of turbine for info screen
- prevTableEntries = 0, -- previous number of turbines in table
- tabTxt = {}, -- old text boxes of table screen
- infoTxt = nil -- old text boxes of info screen
- }
- end
- -- Show table
- if glassesStates.mode == "table" then
- if glassesStates.show ~= "table" then
- -- reset: a brand new box is required.
- glassesStates.prevTableEntries = 0
- end
- displayTableOnGlasses(glasses)
- glassesStates.show = "table"
- return
- end
- -- Info box
- if glassesStates.mode == "info" then
- if glassesStates.show ~= "info" then
- -- reset: a brand new box is required.
- glassesStates.infoTxt = nil
- end
- displayInfoOnGlasses(glasses)
- glassesStates.show = "info"
- return
- end
- -- Hide any screen
- if glassesStates.mode == "hidden" then
- if glassesStates.show == "hidden" then
- return -- already hidden
- end
- glasses.clear()
- glassesStates.show = "hidden"
- return
- end
- -- Show the help box
- if glassesStates.mode == "help" then
- if glassesStates.show == "help" then
- return -- already show help screen
- end
- glasses.clear()
- displayHelpToGlasses(glasses)
- glassesStates.show = "help"
- return
- end
- -- show error message
- if string.sub(glassesStates.mode,1,4) == "err:" then
- if glassesStates.show == glassesStates.mode then
- return -- already showing this message
- end
- glasses.clear()
- displayErrorMsgToGlasses(glasses, string.sub(glassesStates.mode, 5))
- glassesStates.show = glassesStates.mode
- return
- end
- return
- end
- --
- -- Display turbine data to any connected monitor, if possible
- --
- local function displayTable()
- displayTableOnMonitor("#term")
- if pocket then
- -- Pocket computer: ignore monitors
- return
- end
- local glassesName = nil
- local pList = peripheral.getNames()
- local i, name
- for i, name in pairs(pList) do
- if peripheral.getType(name) == "monitor" then
- -- found monitor as peripheral
- displayTableOnMonitor(name)
- end
- if peripheral.getType(name) == "openperipheral_glassesbridge" then
- -- found terminal glasses
- glassesName = name
- end
- end
- if glassesName then
- -- Display table and other stuff on terminal glasses
- displayOnGlasses(glassesName)
- end
- end
- --
- -- Cycle informations on monitor
- --
- -- Parameters:
- -- monName - Name of monitor to cycle the informations
- --
- local function doCycleInformations(monName)
- if cycleDisplayStates[monName] == nil then
- return -- not a cycled (small) monitor
- end
- if cycleDisplayStates[monName] == "RPM" then
- cycleDisplayStates[monName] = "Flow"
- elseif cycleDisplayStates[monName] == "Flow" then
- cycleDisplayStates[monName] = "EnergyRate"
- elseif cycleDisplayStates[monName] == "EnergyRate" then
- cycleDisplayStates[monName] = "EnergyStored"
- else
- cycleDisplayStates[monName] = "RPM"
- end
- displayTable()
- end
- --
- -- Display sub window to change target speed
- --
- -- Parameters:
- -- name - monitor name or "#term"
- -- xPos - x-Position of click
- -- yPos - y-Position of click
- --
- -- Return:
- -- turbine state, new speed
- --
- local function changeSpeed(monName, xPos, yPos)
- if yPos < 3 then
- doCycleInformations(monName)
- return nil,-1
- end
- local mon
- if monName == "#term" then
- mon = term -- terminal
- else
- mon = peripheral.wrap(monName) -- wrap monitor
- end
- local monWidth, monHeight = mon.getSize()
- if yPos == monHeight and cycleDisplayStates[monName] ~= nil then
- doCycleInformations(monName)
- return nil,-1
- end
- local label = labelArr[yPos - 2]
- if label == nil
- then
- return nil,-1
- end
- local tState = turbineTable[label]
- if tState == nil or not tState.remoteControl then
- return nil,-1
- end
- local dispX = xPos - 11
- local dispY = yPos - 3
- if dispX < 1 then
- dispX = 1
- end
- if dispX + 23 > monWidth then
- dispX = monWidth - 22
- end
- if dispY < 1 then
- dispY = 1
- end
- if dispY + 5 > monHeight then
- dispY = monHeight - 4
- end
- mon.setCursorPos(dispX, dispY)
- mon.write("+---------------------+")
- mon.setCursorPos(dispX, dispY+1)
- mon.write("| ")
- mon.setTextColor(colors.orange)
- mon.write("Change target speed")
- mon.setTextColor(colors.white)
- mon.write(" |")
- mon.setCursorPos(dispX, dispY+2)
- mon.write("| ")
- mon.setTextColor(colors.orange)
- mon.write("of ")
- mon.setTextColor(colors.yellow)
- mon.write(format_str(label, 16))
- mon.setTextColor(colors.white)
- mon.write(" |")
- mon.setCursorPos(dispX, dispY+3)
- mon.write("| ")
- mon.setTextColor(colors.red)
- if tState.tSpeed == 0 then
- mon.setBackgroundColor(colors.blue)
- end
- mon.write(" OFF ")
- mon.setBackgroundColor(colors.black)
- mon.write(" ")
- mon.setTextColor(colors.green)
- if tState.tSpeed == 900 then
- mon.setBackgroundColor(colors.blue)
- end
- mon.write(" 900 ")
- mon.setBackgroundColor(colors.black)
- mon.write(" ")
- if tState.tSpeed == 1800 then
- mon.setBackgroundColor(colors.blue)
- end
- mon.write(" 1800 ")
- mon.setBackgroundColor(colors.black)
- mon.setTextColor(colors.white)
- mon.write(" |")
- mon.setCursorPos(dispX, dispY+4)
- mon.write("+---------------------+")
- local evt, p1, p2, p3
- repeat
- -- Event loop
- evt, p1, p2, p3 = os.pullEvent()
- until (evt == "mouse_click" and monName == "#term")
- or (evt == "monitor_touch" and p1 == monName)
- local newSpeed = -1
- if p3 == dispY+3 then
- if p2 >= dispX+2 and p2 <= dispX+6 then
- -- OFF
- newSpeed = 0
- elseif p2 >= dispX+9 and p2 <= dispX+13 then
- -- 900
- newSpeed = 900
- elseif p2 >= dispX+15 and p2 <= dispX+20 then
- -- 1800
- newSpeed = 1800
- end
- return tState, newSpeed
- end
- return nil, -1
- end
- --
- -- Parse an execute turbine commends
- --
- -- Parameters:
- -- cmdline - the chat command
- --
- local function parseAndExecGlassesCommands(cmdline)
- local args = {}
- local cmd=nil
- for arg in string.gmatch(cmdline, "([^ ]*)[ ]*") do
- if cmd == nil then
- cmd = arg
- else
- table.insert(args, arg)
- end
- end
- if cmd == "hide" then
- glassesStates.mode = "hidden"
- elseif cmd == "show" then
- glassesStates.mode = "table"
- if args[1] ~= nil then
- local tName = args[1]
- local i=2
- while args[i] ~= nil do
- tName = tName .. " " .. args[i]
- i = i + 1
- end
- local tState = turbineTable[tName]
- if tState == nil then
- if string.sub(glassesStates.mode,1,4) ~= "err:" then
- glassesStates.recoverMode = glassesStates.mode
- end
- glassesStates.mode = "err:turbine_not_found"
- return
- end
- glassesStates.mode = "info"
- glassesStates.infoTurbine = tName
- end
- elseif cmd == "help" then
- glassesStates.mode = "help"
- elseif cmd == "speed" then
- local speed = args[1]
- local tName = args[2]
- if tName == nil then
- glassesStates.mode = "help"
- return
- end
- local i=3
- while args[i] ~= nil do
- tName = tName .. " " .. args[i]
- i = i + 1
- end
- local tState = turbineTable[tName]
- if tState == nil then
- if string.sub(glassesStates.mode,1,4) ~= "err:" then
- glassesStates.recoverMode = glassesStates.mode
- end
- glassesStates.mode = "err:turbine_not_found"
- return
- end
- if not tState.remoteControl then
- if string.sub(glassesStates.mode,1,4) ~= "err:" then
- glassesStates.recoverMode = glassesStates.mode
- end
- glassesStates.mode = "err:remote_control_disabled"
- return
- end
- if speed == nil then
- if string.sub(glassesStates.mode,1,4) ~= "err:" then
- glassesStates.recoverMode = glassesStates.mode
- end
- glassesStates.mode = "err:speed_missing"
- return
- end
- local newSpeed
- if speed == "off" or speed == "0" then
- newSpeed = 0
- elseif speed == "900" then
- newSpeed = 900
- elseif speed == "1800" then
- newSpeed = 1800
- else
- if string.sub(glassesStates.mode,1,4) ~= "err:" then
- glassesStates.recoverMode = glassesStates.mode
- end
- glassesStates.mode = "err:invalid_turbine_speed"
- return
- end
- sendSpeedChange(tState.computerId, newSpeed)
- if string.sub(glassesStates.mode,1,4) == "err:" then
- glassesStates.mode = glassesStates.recoverMode
- end
- else
- glassesStates.mode = "help"
- return
- end
- end
- while true do
- sendBroadcastRequest()
- local tID = os.startTimer(loopT)
- turbineTable = {}
- repeat
- -- Event loop
- local evt, p1, p2, p3, p4, p5 = os.pullEvent()
- if evt == "modem_message"
- and p2 == os.getComputerID()
- and p3 == stateRequestChannel
- then
- local rState = textutils.unserialize(tostring(p4))
- if rState ~= nil
- and type(rState) == "table"
- and rState.version ~= nil
- and rState.version == "Turbine V0.1"
- then
- -- seems to be a suitable data structure
- turbineTable[rState.label] = rState
- end
- elseif evt == "monitor_touch" or evt == "mouse_click" then
- local tState, newSpeed
- if evt == "monitor_touch" then
- -- Monitor click
- tState, newSpeed = changeSpeed(p1, p2, p3)
- else
- -- Computer click
- tState, newSpeed = changeSpeed("#term", p2, p3)
- end
- if tState ~= nil then
- sendSpeedChange(tState.computerId, newSpeed)
- end
- -- Sumulate timer event
- evt = "timer"
- p1 = tID
- elseif evt == "char" and pocket then
- -- pocket computers: cycle display
- if p1 == " " or p1 == "\n" or p1 == "\t" then
- doCycleInformations("#term")
- end
- elseif evt == "chat_command" then
- parseAndExecGlassesCommands(p1)
- end
- until evt == "timer" and p1 == tID
- displayTable()
- end
- --
- -- EOF
- --
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement