View difference between Paste ID: 3iZsEpDU and RcBGNSgb
SHOW: | | - or go back to the newest paste.
1
--
2
-- Master control and monitor turbines of a active cooled
3
-- Big Reactor (http://big-reactors.com/).
4
--
5
-- Author: kla_sch
6
--
7
-- History:
8
--     v0.1, 2014-12-29:
9
--         - first version
10
--
11
--     v0.2, 2015-01-02
12
--         - smaller monitors (25 characters) and portable computers supported.
13
--         - change table layout to fit 50 characters per line.
14
--         - minor bugfixes
15
--         - code improvments (thanks to wieselkatze)
16
--
17
--     v0.3, 2015-01-05
18
--         - now support OpenPeripheral Terminal Glasses
19
--         - minor bugfixes
20
--
21
-- Save as "startup"
22
23
--
24
-- Constant values (configuration)
25
--
26
27
-- Loop time
28
local loopT=1
29
30
-- Modem channel to send status requests to any turbine.
31
local stateRequestChannel = 32768 -- Broadcast channel.
32
33
34
-- Position of display on terminal glasses.
35
-- The position 0,0 (posX, posY) is the upper left corner of the display.
36
-- There are also some other basic style values.
37
local tGlassStyle = {
38
   posX = 5,         -- Horizontal position from the left side
39
   posY = 5,         -- Vertical position from top
40
   lineHeight = 8,   -- Height of line
41
   rulerHeight = 2,  -- Height of ruler
42
   borderWidth = 2,  -- Width of box border
43
   padding = 2,      -- Padding between box and text
44
   spacing = 2       -- Spacing between lines
45
}
46
47
48
49
--
50
-- Internal values:
51
--
52
53
local turbineTable = {} -- state of turbines
54
local labelArr = {} -- sorted array of turbine computer labels
55
56
local glassesStates = nil -- table to hold state of the terminal glasses
57
58
-- Cycle flag for small monitors (width of 25 chars)
59
local cycleDisplayStates = {}
60
61
--
62
-- Send modem request to any attached modem
63
--
64
local function sendBroadcastRequest()
65
   local pList = peripheral.getNames()
66
   local i, name
67
   for i, name in pairs(pList) do
68
      if peripheral.getType(name) == "modem" then
69
         peripheral.call(name, "open", os.getComputerID())
70
         peripheral.call(name, "transmit",
71
                         stateRequestChannel, os.getComputerID(),
72
                         "BR_turbine_get_state")
73
      end
74
   end
75
end
76
77
78
--
79
-- Send turbine speed change request to any attached modem
80
--
81
-- Parameters:
82
--     computerId - Id of turbine controller
83
--     speed      - new speed
84
--
85
local function sendSpeedChange(computerId, speed)
86
   local pList = peripheral.getNames()
87
   local i, name
88
   for i, name in pairs(pList) do
89
      if peripheral.getType(name) == "modem" then
90
         peripheral.call(name, "open", os.getComputerID())
91
         peripheral.call(name, "transmit",
92
                         computerId, os.getComputerID(),
93
                         "BR_turbine_set_speed:" .. speed)
94
      end
95
   end
96
end
97
98
99
--
100
-- Format integer value, align right. (repace buggy string.format)
101
--
102
-- Parameters:
103
--     number - The output value
104
--     width  - Output width
105
--
106
-- Return:
107
--     Formated string
108
--
109
local function format_int(number, width)
110
   local result = tostring(math.floor(number + 0.5))
111
   if string.len(result) < width then
112
      result = string.rep(" ", width - string.len(result)) .. result
113
   end
114
115
   return result
116
end
117
118
119
--
120
-- Format string value, align left, cut off. (repace buggy string.format)
121
--
122
-- Parameters:
123
--     str   - the output value
124
--     width - output width
125
--
126
-- Return:
127
--     formated string
128
--
129
local function format_str(str, width)
130
   if string.len(str) < width then -- fill
131
      result = str .. string.rep(" ", width - string.len(str))
132
   elseif string.len(str) > width then -- cut
133
      result = string.sub(str, 1, width)
134
   else
135
      result = str
136
   end
137
138
   return result
139
end
140
141
142
--
143
-- Glasses: Format string value, align left, cutt off.
144
--
145
-- This function does not work well, if you use a buggy version of
146
-- Open Peripherals. Older versions returns the wrong width of the string.
147
--
148
-- Parameters:
149
--     glasses - Wraped glasses bridge
150
--     str   - the output value
151
--     width - maximum output width (pixed)
152
--
153
-- Returns: A string that fits
154
--
155
local function format_gl_str(glasses, str, width)
156
   local strLen = glasses.getStringWidth(str)
157
   if strLen <= width then
158
      return str -- It fits
159
   end
160
161
   -- Too long: Test differnet lengths
162
   local minLen = 1
163
   local maxLen = string.len(str)
164
   while maxLen - minLen > 1 do
165
      local testLen = minLen + math.floor( (maxLen - minLen) / 2)
166
  
167
      if glasses.getStringWidth(string.sub(str, 1, testLen)) > width then
168
         maxLen = testLen
169
      else
170
         minLen = testLen
171
      end
172
   end
173
174
   return string.sub(str, 1, minLen)
175
end
176
177
--
178
-- Format float value, align right. (repace buggy string.format)
179
--
180
-- Parameters:
181
--     number    - the output value
182
--     width     - output width
183
--     precision - precision of number
184
--
185
-- Return:
186
--     formated string
187
--
188
local function format_float(number, width, precision)
189
   local pExp = 10 ^ precision
190
   local result = tostring(math.floor(number * pExp + 0.5) / pExp)
191
192
   local s,e = string.find(result, ".", 1, true)
193
   if s == nil then
194
      result = result .. "." .. string.rep("0", precision)
195
   else
196
      local pLen = string.len(result) - s
197
      if pLen < precision then -- fill with '0'
198
         result = result .. string.rep("0", precision - pLen)
199
      end
200
   end
201
202
   if string.len(result) < width then -- fill with ' '
203
      result = string.rep(" ", width - string.len(result)) .. result
204
   end
205
206
   return result
207
end
208
209
210
211
--
212
-- Write text with colors, if possible (advance monitor)
213
--
214
-- Parameters:
215
--     mon   - handle of monitor
216
--     color - text color
217
--     text  - text to write
218
--
219
local function writeColor(mon, color, text)
220
   if mon.isColor() then
221
      mon.setTextColor(color)
222
   end
223
   mon.write(text)
224
   if mon.isColor() then
225
      mon.setTextColor(colors.white)
226
   end
227
end
228
229
230
--
231
-- Scale the monitor text size to needed size of output text.
232
--
233
-- This function try to scale the monitor text size, so that it is enoth for
234
-- "optHeight" lines with "optWidth" characters. If it is not possible
235
-- (text scale is not supported or the connected monitor is too small),
236
-- it also accept "minHeight" lines and "minWidth" characters.
237
--
238
-- Parameters:
239
--     mon        - handle of monitor.
240
--     minWidth   - Minimum number of columns needed.
241
--     optWidth   - Optimal number of columns desired.
242
--     minHeight  - Minimum number of rows needed.
243
--     optHeight  - Optimal number of rows desired.
244
--
245
-- Return:
246
--     Size of monitor after scaling: width, height.
247
--     If the monitor is too small, it returns nul,nil.
248
--
249
local function scaleMonitor(mon, minWidth, optWidth, minHeight, optHeight)
250
   if mon.setTextScale ~= nil then
251
       mon.setTextScale(1)
252
   end
253
254
   local width, height = mon.getSize()
255
256
   if mon.setTextScale == nil then
257
      -- Scale not available
258
      if width < minWidth or height < minHeight then
259
         return nil, nil -- too small
260
      else
261
         return width, height
262
      end
263
   end
264
265
   if width < optWidth or height < optHeight then
266
      -- too small: try to scale down.
267
      mon.setTextScale(0.5)
268
269
      width, height = mon.getSize()
270
      if width < minWidth or height < minHeight then
271
         return nil, nil -- still too small
272
      end
273
   else
274
      -- Huge monitors? Try to scale up, if possible (max scale=5).
275
      local scale = math.min(width / optWidth, height / optHeight, 5)
276
      scale = math.floor(scale * 2) / 2 -- multiple of 0.5
277
278
      if scale > 1 then
279
         mon.setTextScale(scale)
280
         width, height = mon.getSize()
281
      end
282
   end
283
284
   return width, height
285
end
286
287
288
--
289
-- Display turbine data to monitor
290
--
291
-- Parameters:
292
--     mon - Name of monitor. "#term" for computer terminal.
293
--
294
local function displayTableOnMonitor(monName)
295
   local mon
296
297
   if monName == "#term" then
298
      mon = term
299
   else
300
      mon = peripheral.wrap(monName)
301
   end
302
303
   mon.clear()
304
305
   local width, height = scaleMonitor(mon, 25, 50, 5, 5)
306
307
   if width == nil or height == nil then
308
      return -- Montitor is too small
309
   end
310
311
   local cycleState = nil
312
313
   if width < 50 then
314
      -- small monitor
315
      height = height - 1 -- one line for information
316
317
      cycleState = cycleDisplayStates[monName]
318
319
      if cycleState == nil then
320
         -- initialize cycle state
321
         cycleDisplayStates[monName] = "RPM"
322
      end
323
324
      if cycleState == "RPM" then
325
         mon.setCursorPos(1,1)
326
         mon.write("Label          TRPM ARPM")
327
         mon.setCursorPos(1,2)
328
         mon.write("============== ==== ====")
329
      elseif cycleState == "Flow" then
330
         mon.setCursorPos(1,1)
331
         mon.write("Label          MFlow AFlow")
332
         mon.setCursorPos(1,2)
333
         mon.write("============== ===== =====")
334
      elseif cycleState == "EnergyRate" then
335
         mon.setCursorPos(1,1)
336
         mon.write("Label          kRF/t")
337
         mon.setCursorPos(1,2)
338
         mon.write("============== =====")
339
      elseif cycleState == "EnergyStored" then
340
         mon.setCursorPos(1,1)
341
         mon.write("Label          Energy")
342
         mon.setCursorPos(1,2)
343
         mon.write("============== =======")
344
      end
345
346
      mon.setCursorPos(1, height + 1)
347
      if pocket then
348
         mon.write("* Press space to cycle")
349
      else
350
         mon.write("* Click here to cycle")
351
      end
352
   else
353
      cycleDisplayStates[monName] = nil -- full sized monitor
354
355
      mon.setCursorPos(1,1)
356
      mon.write("Label          TRPM ARPM MFlow AFlow kRF/t Energy ")
357
      mon.setCursorPos(1,2)
358
      mon.write("============== ==== ==== ===== ===== ===== =======")
359
   end
360
361
   local i, label, tState
362
363
   labelArr = {}
364
   for label, tState in pairs(turbineTable) do
365
      table.insert(labelArr, label)
366
   end
367
368
   table.sort(labelArr)
369
370
   local line = 3
371
   for i, label in pairs(labelArr) do
372
      tState = turbineTable[label]
373
374
      if line <= height then
375
         mon.setCursorPos(1, line)
376
377
         -- prefix
378
         if tState.active then
379
            label = "+" .. label
380
            writeColor(mon, colors.green, format_str(label, 14))
381
         else
382
            label = "-" .. label
383
            writeColor(mon, colors.red, format_str(label, 14))
384
         end
385
386
387
         if cycleState == nil or cycleState == "RPM" then
388
            -- target speed
389
            if tState.tSpeed == 0 then
390
               writeColor(mon, colors.red, "  OFF")
391
            else
392
               mon.write(" "..format_int(tState.tSpeed, 4))
393
            end
394
            
395
            -- actual speed
396
            if math.floor(tState.rotorSpeed*10 + 0.5)/10 == tState.tSpeed then
397
               writeColor(mon, colors.green,
398
                          " " ..format_int(tState.rotorSpeed, 4))
399
            elseif tState.rotorSpeed > 2000 then
400
               writeColor(mon, colors.red,
401
                          " " ..format_int(tState.rotorSpeed, 4))
402
            else
403
               writeColor(mon, colors.orange,
404
                          " " ..format_int(tState.rotorSpeed, 4))
405
            end
406
         end
407
408
         if cycleState == nil or cycleState == "Flow" then
409
            -- steam flow
410
            mon.write(" "..format_int(tState.fluidFlowRateMax, 5))
411
412
            if tState.fluidFlowRate == tState.fluidFlowRateMax then
413
               writeColor(mon, colors.green,
414
                          " "..format_int(tState.fluidFlowRate, 5))
415
            else
416
               writeColor(mon, colors.orange,
417
                          " "..format_int(tState.fluidFlowRate, 5))
418
            end
419
420
         end
421
422
         if cycleState == nil or cycleState == "EnergyRate" then
423
            -- energy rate
424
            mon.write(" "..format_float(tState.energyProducedLastTick / 1000,
425
                                        5, 2))
426
         end
427
428
         if cycleState == nil or cycleState == "EnergyStored" then
429
            -- energy stored
430
            mon.write(" "..format_int(tState.energyStored, 7))
431
         end
432
433
         line = line + 1
434
      end
435
   end
436
end
437
438
439
--
440
-- Displays a bordered box
441
--
442
-- Parameters:
443
--     glasses - wraped glasses bridge
444
--     width   -- outer width of the box
445
--     height  -- inner width of the box
446
--
447
local function displayBoxToGlasses(glasses, width, height)
448
   glasses.addBox(tGlassStyle.posX, tGlassStyle.posY,
449
                  width, height, 0xFFFF00, 0.5)
450
   width = width - 2 * tGlassStyle.borderWidth
451
   height = height - 2 * tGlassStyle.borderWidth
452
   glasses.addBox(tGlassStyle.posX + tGlassStyle.borderWidth,
453
                  tGlassStyle.posY + tGlassStyle.borderWidth,
454
                  width, height, 0x0000FF, 0.5)
455
end
456
457
458
--
459
-- Display a help box on terminal glasses
460
--
461
-- Parameters:
462
--     glasses - wraped glasses bridge
463
--
464
local function displayHelpToGlasses(glasses)
465
   glasses.clear()
466
467
   local boxHeight =
468
      2 * tGlassStyle.borderWidth                          -- two borders
469
      + 2 * tGlassStyle.padding                            -- two paddings
470
      + 4 * tGlassStyle.lineHeight                         -- toppic
471
      + 9 * (tGlassStyle.lineHeight + tGlassStyle.padding) -- 8 lines
472
473
   displayBoxToGlasses(glasses, 300, boxHeight)
474
475
   local textX =
476
      tGlassStyle.posX + tGlassStyle.borderWidth + tGlassStyle.padding
477
   local textY =
478
      tGlassStyle.posY + tGlassStyle.borderWidth + tGlassStyle.padding
479
480
   local tabStopWidth = 120
481
482
   local header =
483
      glasses.addText(textX, textY, "Terminal Glasses Commands")
484
   header.setScale(2)
485
486
   textY = textY + 4 * tGlassStyle.lineHeight
487
488
   glasses.addText(textX, textY, "$$help")
489
   glasses.addText(textX + tabStopWidth, textY,
490
                   "open this help screen")
491
492
   textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
493
494
   glasses.addText(textX, textY, "$$hide")
495
   glasses.addText(textX + tabStopWidth, textY,
496
                   "hide all screens")
497
   
498
   textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
499
500
   glasses.addText(textX, textY, "$$show")
501
   glasses.addText(textX + tabStopWidth, textY,
502
                   "show table with turbines")
503
504
   textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
505
506
   glasses.addText(textX, textY, "$$show <name>")
507
   glasses.addText(textX + tabStopWidth, textY,
508
                   "show turbine data of <name>")
509
510
   textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
511
512
   glasses.addText(textX, textY, "$$speed off <name>")
513
   glasses.addText(textX + tabStopWidth, textY,
514
                   "set turbine <name> to off")
515
516
   textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
517
518
   glasses.addText(textX, textY, "$$speed 900 <name>")
519
   glasses.addText(textX + tabStopWidth, textY,
520
                   "set turbine <name> to 900 RPM")
521
522
   textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
523
524
   glasses.addText(textX, textY, "$$speed 1800 <name>")
525
   glasses.addText(textX + tabStopWidth, textY,
526
                   "set turbine <name> to 1800 RPM")   
527
528
   textY = textY + 2 * (tGlassStyle.lineHeight + tGlassStyle.spacing)
529
530
   glasses.addText(textX, textY, "Exit this help with $$show or $$hide")
531
end
532
533
534
535
--
536
-- Display error message on terminal glasses
537
--
538
-- Parameters:
539
--     glasses - wraped glasses bridge
540
--     msgId   - identifier of error
541
--
542
local function displayErrorMsgToGlasses(glasses, msgId)
543
   glasses.clear()
544
545
   local boxHeight =
546
      2 * tGlassStyle.borderWidth                          -- two borders
547
      + 2 * tGlassStyle.padding                            -- two paddings
548
      + 4 * tGlassStyle.lineHeight                         -- toppic
549
      + 3 * (tGlassStyle.lineHeight + tGlassStyle.padding) -- 3 lines
550
551
   displayBoxToGlasses(glasses, 280, boxHeight)
552
553
   local textX =
554
      tGlassStyle.posX + tGlassStyle.borderWidth + tGlassStyle.padding
555
   local textY =
556
      tGlassStyle.posY + tGlassStyle.borderWidth + tGlassStyle.padding
557
558
   local header =
559
      glasses.addText(textX, textY, "Command Error")
560
   header.setScale(2)
561
562
   textY = textY + 4 * tGlassStyle.lineHeight
563
564
   local msgText
565
   if msgId == "turbine_not_found" then
566
      msgText = "The turbine you entered was not found in table"
567
   elseif msgId == "remote_control_disabled" then
568
      msgText = "Remote control feature disabled at turbine"
569
   elseif msgId == "speed_missing" then
570
      msgText = "New target speed is missing"
571
   elseif msgId == "invalid_turbine_speed" then
572
      msgText = "Invalid turbine speed value"
573
   end
574
   glasses.addText(textX, textY, msgText, 0xF2B233)
575
576
   textY = textY + 2 * tGlassStyle.lineHeight
577
   
578
   glasses.addText(textX, textY, "Type in $$help for help or try again")
579
end
580
581
--
582
-- Display single information line to terminal glasses
583
--
584
-- Parameters:
585
--     glasses - wraped handler of terminal glasses bridge
586
--     x       - X-Position of label
587
--     y       - Y-Position of line
588
--     lable   - Label of information line
589
--     value   - value of information line
590
--     color   - color for value
591
--     update  - flag: only update information
592
--
593
local function displayInfoLineOnGlasses(glasses, x, y, label, value,
594
                                        color, update)
595
   local tabStopWidth = 100
596
   if update then
597
      glassesStates.infoTxt[label].setText(value)
598
   else
599
      glasses.addText(x, y, label)
600
      glassesStates.infoTxt[label] =
601
         glasses.addText(x + tabStopWidth, y, value)
602
   end
603
604
   glassesStates.infoTxt[label].setColor(color)
605
end
606
607
608
--
609
-- Display any known informations of a single turbine to terminal glasses
610
--
611
-- Parameters:
612
--     glasses - wraped handler of terminal glasses bridge
613
--
614
local function displayInfoOnGlasses(glasses)
615
   if glassesStates.infoTurbine == nil then
616
      glassesStates.show = "table"
617
      return -- no turbine name? ups!
618
   end
619
620
   local tState = turbineTable[glassesStates.infoTurbine]
621
622
   local noInfo = "<no information>"
623
   local textX =
624
      tGlassStyle.posX + tGlassStyle.borderWidth + tGlassStyle.padding
625
   local textY =
626
      tGlassStyle.posY + tGlassStyle.borderWidth + tGlassStyle.padding
627
628
   local updateOnly = true
629
   if glassesStates.infoTxt == nil then
630
      -- need a new box
631
      updateOnly = false
632
      glassesStates.infoTxt = {}
633
634
      glasses.clear()
635
636
      local boxHeight =
637
         2 * tGlassStyle.borderWidth                           -- two borders
638
         + 2 * tGlassStyle.padding                             -- two paddings
639
         + 4 * tGlassStyle.lineHeight                          -- toppic
640
         + 13 * (tGlassStyle.lineHeight + tGlassStyle.padding) -- 13 lines
641
642
      displayBoxToGlasses(glasses, 240, boxHeight)
643
644
      local header =
645
         glasses.addText(textX, textY, "Turbine information")
646
      header.setScale(2)
647
648
      textY = textY + 4 * tGlassStyle.lineHeight
649
   end
650
651
   local outStr, color
652
653
   -- Label
654
   outStr = glassesStates.infoTurbine
655
   if tState ~= nil and tState.active then
656
      color=0x00FF00 -- green
657
   else
658
      color=0xFF0000 -- red
659
   end
660
661
   displayInfoLineOnGlasses(glasses, textX, textY,
662
                            "Label", outStr, color, updateOnly)
663
664
   textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
665
   
666
   -- Active state
667
   if tState ~= nil and tState.active then
668
      outStr = "Activated"
669
      color = 0x00FF00
670
   elseif tState == nil then
671
      outStr = "Turbine not reachable"
672
      color = 0xFF0000
673
   else
674
      outStr = "Deactivated"
675
      color = 0xFF0000
676
   end
677
678
   displayInfoLineOnGlasses(glasses, textX, textY,
679
                            "State", outStr, color, updateOnly)
680
   
681
   textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
682
   
683
   -- Remote Control
684
   if tState then
685
      if tState.remoteControl then
686
         outStr = "Activated"
687
         color = 0x00FF00 -- green
688
      else
689
         outStr = "Deactivated"
690
         color = 0xFF0000 -- red
691
      end
692
   else
693
      outStr = noInfo
694
      color = 0xFF0000 -- red
695
   end
696
   
697
   displayInfoLineOnGlasses(glasses, textX, textY,
698
                            "Remote Control", outStr, color, updateOnly)
699
   
700
   textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
701
702
   -- Target Speed
703
   if tState then
704
      outStr = tostring(math.floor(tState.tSpeed + 0.5)) .. " RPM"
705
      color = 0xFFFFFF -- white
706
   else
707
      outStr = noInfo
708
      color = 0xFF0000 -- red
709
   end
710
711
   displayInfoLineOnGlasses(glasses, textX, textY,
712
                            "Target Speed", outStr, color, updateOnly)
713
   textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
714
   
715
   -- Actual Speed
716
   if tState then
717
      local rotorSpeed = math.floor(tState.rotorSpeed*10+ 0.5) /10
718
      outStr = tostring(rotorSpeed) .. " RPM"
719
      if rotorSpeed == tState.tSpeed
720
      then
721
         color = 0x00FF00 -- green
722
      elseif rotorSpeed >= 2000 then
723
         color = 0xFF0000 -- red
724
      else
725
         color = 0xF2B233 -- orange
726
      end
727
   else
728
      outStr = noInfo
729
      color = 0xFF0000
730
   end
731
732
   displayInfoLineOnGlasses(glasses, textX, textY,
733
                            "Actual Speed", outStr, color, updateOnly)
734
   textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
735
736
   -- Maximum Flow Rate
737
   if tState then
738
      local flowRateMax =
739
         math.floor(tState.fluidFlowRateMax * 10 + 0.5) / 10
740
      local flowRateMaxMax =
741
         math.floor(tState.fluidFlowRateMaxMax * 10 + 0.5) / 10
742
743
      outStr =
744
         tostring(flowRateMax) .. " mB/t"
745
         .." of " .. tostring(flowRateMaxMax) .. " mB/t"
746
      color = 0xFFFFFF -- white
747
   else
748
      outStr = noInfo
749
      color = 0xFF0000 -- red
750
   end
751
   displayInfoLineOnGlasses(glasses, textX, textY,
752
                            "Max Flow Rate", outStr, color, updateOnly)
753
   textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
754
755
   -- Actual Flow Rate
756
   if tState then
757
      local fluidFlowRate = math.floor(tState.fluidFlowRate * 10 + 0.5) / 10
758
      outStr = tostring(fluidFlowRate) .. " mB/t"
759
      if tState.fluidFlowRateMax == tState.fluidFlowRate then
760
         color = 0x00FF00 -- green
761
      else
762
         color = 0xF2B233 -- orange
763
      end
764
   else
765
      outStr = noInfo
766
      color = 0xFF0000 -- red
767
   end
768
769
   displayInfoLineOnGlasses(glasses, textX, textY,
770
                            "Act Flow Rate", outStr, color, updateOnly)
771
   textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
772
773
   -- Energy Output
774
   if tState then
775
      local energyOut = math.floor(tState.energyProducedLastTick + 0.5)
776
      outStr = tostring(energyOut) .. " RF/t"
777
      color = 0xFFFFFF -- white
778
   else
779
      outStr = noInfo
780
      color = 0xFF0000 -- red
781
   end
782
   
783
   displayInfoLineOnGlasses(glasses, textX, textY,
784
                            "Energy Output", outStr, color, updateOnly)
785
   textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
786
787
   -- Energy Stored
788
   if tState then
789
      local energy = math.floor(tState.energyStored + 0.5)
790
      outStr = tostring(energy) .. " RF"
791
      color = 0xFFFFFF -- white
792
   else
793
      outStr = noInfo
794
      color = 0xFF0000 -- red
795
   end
796
   displayInfoLineOnGlasses(glasses, textX, textY,
797
                            "Energy Stored", outStr, color, updateOnly)
798
   textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
799
   
800
   -- Intake Fluid
801
   if tState then
802
      local inputAmount = math.floor(tState.inputAmount * 10 + 0.5) / 10
803
      local inputType = "-"
804
      if tState.inputType then
805
         inputType = tState.inputType
806
      end
807
      outStr = tostring(inputAmount) .. " mB (" .. inputType .. ")"
808
      color = 0xFFFFFF -- white
809
   else
810
      outStr = noInfo
811
      color = 0xFF0000 -- red
812
   end
813
   displayInfoLineOnGlasses(glasses, textX, textY,
814
                            "Intake Fluid", outStr, color, updateOnly)
815
   textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
816
   
817
   -- Exhaust Fluid
818
   if tState then
819
      local outputAmount = math.floor(tState.outputAmount * 10 + 0.5) / 10
820
      local outputType = "-"
821
      if tState.outputType then
822
         outputType = tState.outputType
823
      end
824
      outStr = tostring(outputAmount) .. " mB (" .. outputType .. ")"
825
      color = 0xFFFFFF -- white
826
   else
827
      outStr = noInfo
828
      color = 0xFF0000 -- red
829
   end
830
   displayInfoLineOnGlasses(glasses, textX, textY,
831
                            "Exhaust Fluid", outStr, color, updateOnly)
832
   textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
833
   
834
   -- Max Fluid Amount
835
   if tState then
836
      local fluidAmount = math.floor(tState.fluidAmountMax * 10 + 0.5) / 10
837
      outStr = tostring(fluidAmount) .. " mB"
838
      color = 0xFFFFFF -- white
839
   else
840
      outStr = noInfo
841
      color = 0xFF0000 -- red
842
   end
843
   displayInfoLineOnGlasses(glasses, textX, textY,
844
                            "Max Fluid Amount", outStr, color, updateOnly)
845
   textY = textY + tGlassStyle.lineHeight + tGlassStyle.spacing
846
   
847
   -- Infuctor
848
   if tState then
849
      if tState.inductorEngaged == nil then
850
         outStr = "<not available>"
851
         color = 0xF2B233 -- orange
852
      elseif tState.inductorEngaged then
853
         outStr = "Engaged"
854
         color = 0x00FF00 -- green
855
      else
856
         outStr = "Disengaged"
857
         color = 0xF2B233 -- orange
858
      end
859
   else
860
      outStr = noInfo
861
      color = 0xFF0000 -- red
862
   end
863
   displayInfoLineOnGlasses(glasses, textX, textY,
864
                            "Inductor", outStr, color, updateOnly)
865
end
866
867
--
868
-- Display table on terminal glasses
869
--
870
-- Parameters:
871
--     glasses - wraped terminal bridge device
872
--
873
local function displayTableOnGlasses(glasses)
874
   labelArr = {}
875
   local label, tState
876
   for label, tState in pairs(turbineTable) do
877
      table.insert(labelArr, label)
878
   end
879
880
   table.sort(labelArr)
881
882
   local updateOnly = (glassesStates.prevTableEntries == #labelArr)
883
   glassesStates.prevTableEntries = #labelArr
884
885
   if not updateOnly then
886
      -- redraw box
887
      local boxWidth =
888
          51 * 8   -- Space enought for 52 characters
889
             + 2 * tGlassStyle.borderWidth     -- two borders (top/bottom)
890
             + 2 * tGlassStyle.padding         -- two padding (top/bottom)
891
892
      local boxHeight =
893
         tGlassStyle.lineHeight -- header line
894
         + tGlassStyle.spacing -- spacing of header line
895
         + tGlassStyle.rulerHeight + tGlassStyle.spacing -- ruler with spacing
896
         + (tGlassStyle.lineHeight + tGlassStyle.spacing)
897
           * #labelArr -- Table lines
898
         + 2 * tGlassStyle.borderWidth -- two borders (left/right)
899
         + 2 * tGlassStyle.padding     -- two paddings (left/right)
900
901
      glasses.clear()
902
      displayBoxToGlasses(glasses, boxWidth, boxHeight)
903
   end
904
905
   -- Table Header 
906
   local textX =
907
      tGlassStyle.posX + tGlassStyle.borderWidth + tGlassStyle.padding
908
   local textY =
909
      tGlassStyle.posY + tGlassStyle.borderWidth + tGlassStyle.padding
910
911
   if not updateOnly then
912
      glasses.addText(textX, textY, "Label")
913
      glasses.addText(textX + (8*16), textY, "TRPM")
914
      glasses.addText(textX + (8*21), textY, "ARPM")
915
      glasses.addText(textX + (8*26), textY, "MFlow")
916
      glasses.addText(textX + (8*32), textY, "AFlow")
917
      glasses.addText(textX + (8*38), textY, "kRF/t")
918
      glasses.addText(textX + (8*44), textY, "Energy")
919
920
      glasses.addBox(tGlassStyle.posX
921
                        + tGlassStyle.borderWidth,
922
                     tGlassStyle.posY
923
                        + tGlassStyle.borderWidth
924
                        + tGlassStyle.padding
925
                        + tGlassStyle.lineHeight + tGlassStyle.spacing,
926
                     51 * 8 + 2 * tGlassStyle.padding, 2,
927
                     0xFFFFFF)
928
   end
929
930
   -- Table lines
931
   local i
932
   for i, label in pairs(labelArr) do
933
      tState = turbineTable[label]
934
935
      -- Label
936
      local linePos =
937
         tGlassStyle.posY            -- Y-Pos
938
         + tGlassStyle.borderWidth   -- the border
939
         + tGlassStyle.padding       -- the padding
940
         + tGlassStyle.lineHeight    -- header line
941
         + tGlassStyle.rulerHeight   -- ruler
942
         + 2 * tGlassStyle.spacing   -- 2 * spacings (header+ruler)
943
         + (i-1) * (tGlassStyle.lineHeight + tGlassStyle.spacing)
944
945
      if not glassesStates.tabTxt[i] then
946
         glassesStates.tabTxt[i] = {}
947
      end
948
949
      local outStr = format_gl_str(glasses, label, (15*8))
950
      if updateOnly then
951
         glassesStates.tabTxt[i].label.setText(outStr)
952
      else
953
         glassesStates.tabTxt[i].label =
954
            glasses.addText(textX, linePos, outStr)
955
      end
956
957
      if tState.active then
958
         glassesStates.tabTxt[i].label.setColor(0x00FF00)
959
      else
960
         glassesStates.tabTxt[i].label.setColor(0xFF0000)
961
      end
962
963
      -- TRPM
964
      if tState.tSpeed == 0 then
965
         outStr = "OFF"
966
      else
967
         outStr = tostring(math.floor(tState.tSpeed + 0.5))
968
      end
969
      
970
      if updateOnly then
971
         glassesStates.tabTxt[i].TRPM.setText(outStr)
972
      else
973
         glassesStates.tabTxt[i].TRPM =
974
            glasses.addText(textX + (8*16), linePos, outStr)
975
      end
976
977
      if tState.tSpeed == 0 then
978
         glassesStates.tabTxt[i].TRPM.setColor(0xFF0000) -- red
979
      else
980
         glassesStates.tabTxt[i].TRPM.setColor(0xFFFFFF) -- white
981
      end
982
983
      -- APRM
984
      outStr = tostring(math.floor(tState.rotorSpeed + 0.5))
985
      if updateOnly then
986
         glassesStates.tabTxt[i].ARPM.setText(outStr)
987
      else
988
         glassesStates.tabTxt[i].ARPM =
989
            glasses.addText(textX + (8*21), linePos, outStr)
990
      end
991
992
      if math.floor(tState.rotorSpeed*10 + 0.5)/10 == tState.tSpeed then
993
         glassesStates.tabTxt[i].ARPM.setColor(0x00FF00) -- green
994
      elseif tState.rotorSpeed > 2000 then
995
         glassesStates.tabTxt[i].ARPM.setColor(0xFF0000) -- red
996
      else
997
         glassesStates.tabTxt[i].ARPM.setColor(0xF2B233) -- orange
998
      end
999
         
1000
1001
      -- TFlow
1002
      outStr = tostring(math.floor(tState.fluidFlowRateMax + 0.5))
1003
      if updateOnly then
1004
         glassesStates.tabTxt[i].TFlow.setText(outStr)
1005
      else
1006
         glassesStates.tabTxt[i].TFlow =
1007
            glasses.addText(textX + (8*26), linePos, outStr)
1008
      end
1009
1010
      -- AFlow
1011
      outStr = tostring(math.floor(tState.fluidFlowRate + 0.5))
1012
      if updateOnly then
1013
         glassesStates.tabTxt[i].AFlow.setText(outStr)
1014
      else
1015
         glassesStates.tabTxt[i].AFlow =
1016
            glasses.addText(textX + (8*32), linePos, outStr)
1017
      end
1018
1019
      if tState.fluidFlowRate == tState.fluidFlowRateMax then
1020
         glassesStates.tabTxt[i].AFlow.setColor(0x00FF00) -- green
1021
      else
1022
         glassesStates.tabTxt[i].AFlow.setColor(0xF2B233) -- orange
1023
      end
1024
1025
      -- kRF/t
1026
      local kRFt = tState.energyProducedLastTick / 1000
1027
      outStr = tostring(math.floor(kRFt*100 + 0.5) / 100)
1028
      if updateOnly then
1029
         glassesStates.tabTxt[i].kRFt.setText(outStr)
1030
      else
1031
         glassesStates.tabTxt[i].kRFt =
1032
            glasses.addText(textX + (8*38), linePos, outStr)
1033
      end
1034
1035
      -- Energy
1036
      outStr = tostring(math.floor(tState.energyStored + 0.5))
1037
      if updateOnly then
1038
         glassesStates.tabTxt[i].Energy.setText(outStr)
1039
      else
1040
         glassesStates.tabTxt[i].Energy =
1041
            glasses.addText(textX + (8*44), linePos, outStr)
1042
      end
1043
   end
1044
end
1045
1046
1047
--
1048
-- Display table on terminal glasses
1049
--
1050
-- Parameters:
1051
--     name - name (side) of device
1052
--
1053
local function displayOnGlasses(name)
1054
   local glasses = peripheral.wrap(name)
1055
1056
   if glassesStates == nil then
1057
      -- initialize states of terminal glasses
1058
      glasses.clear()
1059
1060
      glassesStates = {
1061
         mode = "table",         -- mode of display
1062
         show = "hidden",        -- what mode is currently shown
1063
         recoverMode = "hidden", -- mode to recover after speed change error
1064
         infoTurbine = nil,      -- name of turbine for info screen
1065
         prevTableEntries = 0,   -- previous number of turbines in table
1066
         tabTxt = {},            -- old text boxes of table screen
1067
         infoTxt = nil           -- old text boxes of info screen
1068
      }
1069
   end
1070
1071
   -- Show table
1072
   if glassesStates.mode == "table" then
1073
      if glassesStates.show ~= "table" then
1074
         -- reset: a brand new box is required.
1075
         glassesStates.prevTableEntries = 0
1076
      end
1077
1078
      displayTableOnGlasses(glasses)
1079
      glassesStates.show = "table"
1080
      return
1081
   end
1082
1083
   -- Info box
1084
   if glassesStates.mode == "info" then
1085
      if glassesStates.show ~= "info" then
1086
         -- reset: a brand new box is required.
1087
         glassesStates.infoTxt = nil
1088
      end
1089
1090
      displayInfoOnGlasses(glasses)
1091
      glassesStates.show = "info"
1092
      return
1093
   end
1094
1095
1096
   -- Hide any screen
1097
   if glassesStates.mode == "hidden" then
1098
      if glassesStates.show == "hidden" then
1099
         return -- already hidden
1100
      end
1101
1102
      glasses.clear()
1103
      glassesStates.show = "hidden"
1104
      return
1105
   end
1106
1107
   -- Show the help box
1108
   if glassesStates.mode == "help" then
1109
      if glassesStates.show == "help" then
1110
         return -- already show help screen
1111
      end
1112
1113
      glasses.clear()
1114
      displayHelpToGlasses(glasses)
1115
      glassesStates.show = "help"
1116
      return
1117
   end
1118
1119
   -- show error message
1120
   if string.sub(glassesStates.mode,1,4) == "err:" then
1121
      if glassesStates.show == glassesStates.mode then
1122
         return -- already showing this message
1123
      end
1124
      
1125
      glasses.clear()
1126
      displayErrorMsgToGlasses(glasses, string.sub(glassesStates.mode, 5))
1127
      glassesStates.show = glassesStates.mode
1128
      return
1129
   end
1130
1131
   return
1132
end
1133
1134
1135
--
1136
-- Display turbine data to any connected monitor, if possible
1137
--
1138
local function displayTable()
1139
1140
   displayTableOnMonitor("#term")
1141
1142
   if pocket then
1143
      -- Pocket computer: ignore monitors
1144
      return
1145
   end
1146
1147
1148
   local glassesName = nil
1149
   local pList = peripheral.getNames()
1150
   local i, name
1151
   for i, name in pairs(pList) do
1152
      if peripheral.getType(name) == "monitor" then
1153
         -- found monitor as peripheral
1154
         displayTableOnMonitor(name)
1155
      end
1156
      if peripheral.getType(name) == "openperipheral_glassesbridge" then
1157
         -- found terminal glasses
1158
         glassesName = name
1159
      end
1160
   end
1161
1162
   if glassesName then
1163
      -- Display table and other stuff on terminal glasses
1164
      displayOnGlasses(glassesName)
1165
   end
1166
end
1167
1168
--
1169
-- Cycle informations on monitor
1170
--
1171
-- Parameters:
1172
--     monName - Name of monitor to cycle the informations
1173
--
1174
local function doCycleInformations(monName)
1175
   if cycleDisplayStates[monName] == nil then
1176
      return -- not a cycled (small) monitor
1177
   end
1178
1179
   if cycleDisplayStates[monName] == "RPM" then
1180
      cycleDisplayStates[monName] = "Flow"
1181
   elseif cycleDisplayStates[monName] == "Flow" then
1182
      cycleDisplayStates[monName] = "EnergyRate"
1183
   elseif cycleDisplayStates[monName] == "EnergyRate" then
1184
      cycleDisplayStates[monName] = "EnergyStored"
1185
   else
1186
      cycleDisplayStates[monName] = "RPM"
1187
   end
1188
   displayTable()
1189
end
1190
1191
1192
--
1193
-- Display sub window to change target speed
1194
--
1195
-- Parameters:
1196
--    name  - monitor name or "#term"
1197
--    xPos  - x-Position of click
1198
--    yPos  - y-Position of click
1199
--
1200
-- Return:
1201
--    turbine state, new speed
1202
--
1203
local function changeSpeed(monName, xPos, yPos)
1204
   if yPos < 3 then
1205
      doCycleInformations(monName)
1206
      return nil,-1
1207
   end
1208
1209
   local mon
1210
   if monName == "#term" then
1211
      mon = term -- terminal
1212
   else
1213
      mon = peripheral.wrap(monName) -- wrap monitor
1214
   end
1215
   local monWidth, monHeight = mon.getSize()
1216
1217
   if yPos == monHeight and cycleDisplayStates[monName] ~= nil then
1218
      doCycleInformations(monName)
1219
      return nil,-1
1220
   end
1221
1222
   local label = labelArr[yPos - 2]
1223
   if label == nil
1224
   then
1225
      return nil,-1
1226
   end
1227
1228
   local tState = turbineTable[label]
1229
   if tState == nil or not tState.remoteControl then
1230
      return nil,-1
1231
   end
1232
1233
   local dispX = xPos - 11
1234
   local dispY = yPos - 3
1235
1236
   if dispX < 1 then
1237
      dispX = 1
1238
   end
1239
1240
   if dispX + 23 > monWidth then
1241
      dispX = monWidth - 22
1242
   end
1243
1244
   if dispY < 1 then
1245
      dispY = 1
1246
   end
1247
1248
   if dispY + 5 > monHeight then
1249
      dispY = monHeight - 4
1250
   end
1251
1252
   mon.setCursorPos(dispX, dispY)
1253
   mon.write("+---------------------+")
1254
1255
   mon.setCursorPos(dispX, dispY+1)
1256
   mon.write("| ")
1257
   mon.setTextColor(colors.orange)
1258
   mon.write("Change target speed")
1259
   mon.setTextColor(colors.white)
1260
   mon.write(" |")
1261
1262
   mon.setCursorPos(dispX, dispY+2)
1263
   mon.write("| ")
1264
   mon.setTextColor(colors.orange)
1265
   mon.write("of ")
1266
   mon.setTextColor(colors.yellow)
1267
   mon.write(format_str(label, 16))
1268
   mon.setTextColor(colors.white)
1269
   mon.write(" |")
1270
1271
   mon.setCursorPos(dispX, dispY+3)
1272
   mon.write("| ")
1273
   mon.setTextColor(colors.red)
1274
   if tState.tSpeed == 0 then
1275
      mon.setBackgroundColor(colors.blue)
1276
   end
1277
   mon.write(" OFF ")
1278
   mon.setBackgroundColor(colors.black)
1279
   mon.write("  ")
1280
   mon.setTextColor(colors.green)
1281
   if tState.tSpeed == 900 then
1282
      mon.setBackgroundColor(colors.blue)
1283
   end
1284
   mon.write(" 900 ")
1285
   mon.setBackgroundColor(colors.black)
1286
   mon.write(" ")
1287
   if tState.tSpeed == 1800 then
1288
      mon.setBackgroundColor(colors.blue)
1289
   end
1290
   mon.write(" 1800 ")
1291
   mon.setBackgroundColor(colors.black)
1292
   mon.setTextColor(colors.white)
1293
   mon.write(" |")
1294
1295
   mon.setCursorPos(dispX, dispY+4)
1296
   mon.write("+---------------------+")
1297
1298
   local evt, p1, p2, p3
1299
   repeat
1300
      -- Event loop
1301
      evt, p1, p2, p3 = os.pullEvent()
1302
   until (evt == "mouse_click" and monName == "#term")
1303
      or (evt == "monitor_touch" and p1 == monName)
1304
1305
   local newSpeed = -1
1306
   if p3 == dispY+3 then
1307
      if p2 >= dispX+2 and p2 <= dispX+6 then
1308
         -- OFF
1309
         newSpeed = 0
1310
      elseif p2 >= dispX+9 and p2 <= dispX+13 then
1311
         -- 900
1312
         newSpeed = 900
1313
      elseif p2 >= dispX+15 and p2 <= dispX+20 then
1314
         -- 1800
1315
         newSpeed = 1800
1316
      end
1317
1318
      return tState, newSpeed
1319
   end
1320
1321
   return nil, -1
1322
end
1323
1324
1325
--
1326
-- Parse an execute turbine commends
1327
--
1328
-- Parameters:
1329
--     cmdline - the chat command
1330
--
1331
local function parseAndExecGlassesCommands(cmdline)
1332
   local args = {}
1333
1334
   local cmd=nil
1335
   for arg in string.gmatch(cmdline, "([^ ]*)[ ]*") do
1336
      if cmd == nil  then
1337
         cmd = arg
1338
      else
1339
         table.insert(args, arg)
1340
      end
1341
   end
1342
1343
   if cmd == "hide" then
1344
      glassesStates.mode = "hidden"
1345
   elseif cmd == "show" then
1346
      glassesStates.mode = "table"
1347
      if args[1] ~= nil then
1348
         local tName = args[1]
1349
         local i=2
1350
         while args[i] ~= nil do
1351
            tName = tName .. " " .. args[i]
1352
            i = i + 1
1353
         end
1354
         
1355
         local tState = turbineTable[tName]
1356
         if tState == nil then
1357
            if string.sub(glassesStates.mode,1,4) ~= "err:" then
1358
               glassesStates.recoverMode = glassesStates.mode
1359
            end
1360
            glassesStates.mode = "err:turbine_not_found"
1361
            return
1362
         end
1363
         glassesStates.mode = "info"
1364
         glassesStates.infoTurbine = tName
1365
      end
1366
   elseif cmd == "help" then
1367
      glassesStates.mode = "help"
1368
   elseif cmd == "speed" then
1369
      local speed = args[1]
1370
      local tName = args[2]
1371
1372
      if tName == nil then
1373
         glassesStates.mode = "help"
1374
         return
1375
      end
1376
1377
      local i=3
1378
      while args[i] ~= nil do
1379
         tName = tName .. " " .. args[i]
1380
         i = i + 1
1381
      end
1382
1383
      local tState = turbineTable[tName]
1384
      if tState == nil then
1385
         if string.sub(glassesStates.mode,1,4) ~= "err:" then
1386
            glassesStates.recoverMode = glassesStates.mode
1387
         end
1388
         glassesStates.mode = "err:turbine_not_found"
1389
         return
1390
      end
1391
1392
      if not tState.remoteControl then
1393
         if string.sub(glassesStates.mode,1,4) ~= "err:" then
1394
            glassesStates.recoverMode = glassesStates.mode
1395
         end
1396
         glassesStates.mode = "err:remote_control_disabled"
1397
         return
1398
      end
1399
1400
      if speed == nil then
1401
         if string.sub(glassesStates.mode,1,4) ~= "err:" then
1402
            glassesStates.recoverMode = glassesStates.mode
1403
         end
1404
         glassesStates.mode = "err:speed_missing"
1405
         return
1406
      end
1407
1408
      local newSpeed
1409
      if speed == "off" or speed == "0" then
1410
         newSpeed = 0
1411
      elseif speed == "900" then
1412
         newSpeed = 900
1413
      elseif speed == "1800" then
1414
         newSpeed = 1800
1415
      else
1416
         if string.sub(glassesStates.mode,1,4) ~= "err:" then
1417
            glassesStates.recoverMode = glassesStates.mode
1418
         end
1419
         glassesStates.mode = "err:invalid_turbine_speed"
1420
         return
1421
      end
1422
1423
      sendSpeedChange(tState.computerId, newSpeed)
1424
1425
      if string.sub(glassesStates.mode,1,4) == "err:" then
1426
         glassesStates.mode = glassesStates.recoverMode
1427
      end
1428
   else
1429
      glassesStates.mode = "help"
1430
      return
1431
   end
1432
end
1433
1434
1435
while true do
1436
   sendBroadcastRequest()
1437
1438
   local tID = os.startTimer(loopT)
1439
   turbineTable = {}
1440
   repeat
1441
      -- Event loop
1442
      local evt, p1, p2, p3, p4, p5 = os.pullEvent()
1443
      if evt == "modem_message"
1444
         and p2 == os.getComputerID()
1445
         and p3 == stateRequestChannel
1446
      then
1447
         local rState = textutils.unserialize(tostring(p4))
1448
         if rState ~= nil
1449
            and type(rState) == "table"
1450
            and rState.version ~= nil
1451
            and rState.version == "Turbine V0.1"
1452
         then
1453
            -- seems to be a suitable data structure
1454
            turbineTable[rState.label] = rState
1455
         end
1456
      elseif evt == "monitor_touch" or evt == "mouse_click" then
1457
         local tState, newSpeed
1458
         if evt == "monitor_touch" then
1459
            -- Monitor click
1460
            tState, newSpeed = changeSpeed(p1, p2, p3)
1461
         else
1462
            -- Computer click
1463
            tState, newSpeed = changeSpeed("#term", p2, p3)
1464
         end
1465
1466
         if tState ~= nil then
1467
            sendSpeedChange(tState.computerId, newSpeed)
1468
         end
1469
1470
         -- Sumulate timer event
1471
         evt = "timer"
1472
         p1 = tID
1473
      elseif evt == "char" and pocket then
1474
         -- pocket computers: cycle display
1475
         if p1 == " " or p1 == "\n" or p1 == "\t" then
1476
            doCycleInformations("#term")
1477
         end 
1478
      elseif evt == "chat_command" then
1479
         parseAndExecGlassesCommands(p1)
1480
      end
1481
   until evt == "timer" and p1 == tID
1482
   displayTable()
1483
end
1484
1485
--
1486
-- EOF
1487
--