SHOW:
|
|
- or go back to the newest paste.
1 | /* | |
2 | Script for automatic rotor stop when a solar panel or oxygen farm has high output. | |
3 | Version: 1.11 | |
4 | Sigurd Hansen | |
5 | ||
6 | This program vil autostart at compile. Use argument stop or start to manually override | |
7 | ||
8 | */ | |
9 | #region Configuration | |
10 | ||
11 | // HighPwrValue - Set this to your minimum power requirement in kW per panel. (Or oxygen L/min) | |
12 | const float HighPwrValue = 119.4f; | |
13 | ||
14 | // NameOfSolar - Search for solar blocks or oxygen farms with this name. | |
15 | // Maximum one item per array should be returned. Multiple arrays supported. | |
16 | const string NameOfSolar = "SolarMain"; | |
17 | ||
18 | // NameOfRotorX - Search for rotor blocks with this name. Maximum one item per array should be returned. | |
19 | // Multiple arrays supported. | |
20 | const string NameOfRotorX = "RotorX"; | |
21 | ||
22 | // rotorspeed - Maximum speed of rotor. Will be dynamically adjusted down when close to target. | |
23 | // Recommended value: Less that 1.0f due to ingame bug | |
24 | const float rotorspeed = 0.8f; | |
25 | ||
26 | // EnableTwoAxisMode - Enable dual axis mode. | |
27 | // Recommended value: Depends on design. | |
28 | const bool EnableTwoAxisMode = true; | |
29 | ||
30 | // NameOfRotorY - Search for this name for Y axis. Must only find Y axis rotors. | |
31 | // Maximum one item per array should be returned. Multiple arrays supported. | |
32 | const string NameOfRotorY = "RotorY"; | |
33 | ||
34 | // EnableOxygenMode - Enables oxygen mode. | |
35 | const bool EnableOxygenMode = false; | |
36 | ||
37 | // Auto set torque and braking torque to best practice value. | |
38 | // Recommended value: true | |
39 | const bool ForceAutoTorque = true; | |
40 | ||
41 | // Enable LCD Output | |
42 | const bool EnableLCD = true; | |
43 | const string NameOfLCD = "LCDSolar"; | |
44 | ||
45 | // Duplication | |
46 | // Applying rotor values to other rotors as well if set to true. | |
47 | // If unsure, set all to false. Recommended value: Depends on design. | |
48 | const bool EnableDuplicateRotor1 = true; | |
49 | const bool EnableDuplicateRotor2 = true; | |
50 | const bool EnableDuplicateRotor3 = false; | |
51 | ||
52 | // You might want to inverse some rotors, for example on the other side of the axis. | |
53 | // If so, set the value to true. If unsure change later if rotors lock at wrong angle. | |
54 | const bool InverseDuplicateRotor1 = false; | |
55 | const bool InverseDuplicateRotor2 = true; | |
56 | const bool InverseDuplicateRotor3 = false; | |
57 | ||
58 | // Enter the name of the source rotors you want to duplicate. | |
59 | const string NameOfDuplicateSource1 = "RotorY"; | |
60 | const string NameOfDuplicateSource2 = "RotorY"; | |
61 | const string NameOfDuplicateSource3 = "RotorF"; | |
62 | ||
63 | // Enter the name of the destination rotors you want to duplicate. | |
64 | // The code will search for rotors. For example: | |
65 | // Entering RotorZ will duplicate changes to RotorZa, RotorZB, RotorZQ and so forth. | |
66 | const string SearchForDuplicateDest1 = "RotorZ"; | |
67 | const string SearchForDuplicateDest2 = "RotorH"; | |
68 | const string SearchForDuplicateDest3 = "RotorG"; | |
69 | ||
70 | // Auto night mode (Beta) | |
71 | // Turns off rotors if night is detected. Suboptimal axis towards sun might trigger night mode at daytime. | |
72 | // Use at your own risk. Not recommended for ships. | |
73 | const bool AutoNightMode = false; | |
74 | ||
75 | // Trigger this timer at max power | |
76 | const bool ControlCustomTimerAtMaxPower = false; | |
77 | const string NameForCustomtriggerAtMaxPower = "Timertest1"; | |
78 | ||
79 | // Trigger this timer at low power | |
80 | const bool ControlCustomTimerAtLowPower = false; | |
81 | const string NameForCustomtriggerAtLowPower = "Timertest2"; | |
82 | ||
83 | // Changes below this line not recommended. | |
84 | //------------------------------------------------------------ | |
85 | #endregion | |
86 | // Quick fix to disable useless warnings | |
87 | #pragma warning disable 0162 | |
88 | List<IMyTerminalBlock> solarBlocks = new List<IMyTerminalBlock>(); | |
89 | List<IMyTerminalBlock> rotorBlocksX = new List<IMyTerminalBlock>(); | |
90 | List<IMyTerminalBlock> rotorBlocksY = new List<IMyTerminalBlock>(); | |
91 | List<IMyTerminalBlock> Rotors = new List<IMyTerminalBlock>(); | |
92 | List<IMyTerminalBlock> SourceRotors = new List<IMyTerminalBlock>(); | |
93 | List<IMyTerminalBlock> LcdBlocks = new List<IMyTerminalBlock>(); | |
94 | IMyTimerBlock timer2; | |
95 | IMyTimerBlock timer3; | |
96 | bool firstrun = true; | |
97 | bool nightmode = false; | |
98 | long lCount = 0; | |
99 | int delayCount = 0; | |
100 | string textToLCD = ""; | |
101 | ||
102 | public Program() { | |
103 | Runtime.UpdateFrequency |= UpdateFrequency.Update100; | |
104 | } | |
105 | ||
106 | #region Main | |
107 | void Main(string argument) | |
108 | { | |
109 | if (firstrun) { | |
110 | // Echo = text => {}; // Uncommenting this line might increase performance on servers. Will disable echo | |
111 | GridTerminalSystem.SearchBlocksOfName(NameOfSolar, solarBlocks); // Search for Solar Blocks | |
112 | GridTerminalSystem.SearchBlocksOfName(NameOfRotorX, rotorBlocksX); // Search for Rotor Blocks | |
113 | GridTerminalSystem.SearchBlocksOfName(NameOfRotorY, rotorBlocksY); // Search for RotorY Blocks | |
114 | if (EnableLCD) GridTerminalSystem.SearchBlocksOfName(NameOfLCD, LcdBlocks); // Search for LCD Blocks | |
115 | if (solarBlocks.Count == 0) { throw new Exception("Cannot find solar panel. Check name and recompile."); } | |
116 | if (rotorBlocksX.Count == 0) { throw new Exception("Cannot find x-axis rotor blocks. Check name and recompile."); } | |
117 | if (rotorBlocksY.Count == 0 && EnableTwoAxisMode) { throw new Exception("Cannot find y-axis rotor blocks. Check name and recompile."); } | |
118 | if (rotorBlocksY.Count < rotorBlocksX.Count && EnableTwoAxisMode) { | |
119 | int diff = rotorBlocksX.Count - rotorBlocksY.Count; | |
120 | throw new Exception(diff + " Y-axis rotors missing. Fix and recompile."); | |
121 | } | |
122 | if (solarBlocks.Count > rotorBlocksX.Count) { throw new Exception("Too many solar panels found. Check solar panel names."); } | |
123 | Echo ("Init...\nX rotors: " + rotorBlocksX.Count.ToString() + "\nY rotors: " + rotorBlocksY.Count.ToString()); | |
124 | Echo ("Solar panels/Oxygen farms: " + solarBlocks.Count.ToString()); | |
125 | if (EnableTwoAxisMode) Echo ("Dual Axis mode enabled"); | |
126 | if (EnableOxygenMode) Echo ("Oxygen Farm mode ON"); | |
127 | if (EnableDuplicateRotor1 || EnableDuplicateRotor2 || EnableDuplicateRotor3) Echo ("Duplication activated."); | |
128 | if (ControlCustomTimerAtMaxPower) timer2 = GridTerminalSystem.GetBlockWithName(NameForCustomtriggerAtMaxPower) as IMyTimerBlock; | |
129 | if (ControlCustomTimerAtLowPower) timer3 = GridTerminalSystem.GetBlockWithName(NameForCustomtriggerAtLowPower) as IMyTimerBlock; | |
130 | ||
131 | firstrun = false; | |
132 | } | |
133 | if (argument == "start") Runtime.UpdateFrequency |= UpdateFrequency.Update100; | |
134 | else if (argument == "stop") Runtime.UpdateFrequency &= ~UpdateFrequency.Update100; | |
135 | lCount++; | |
136 | if (lCount == 3) { | |
137 | lCount = 0; | |
138 | } else return; | |
139 | textToLCD = "Auto aligning solar panels\n"; | |
140 | bool[] rotorOn = new bool[rotorBlocksX.Count]; // Stop or start rotor | |
141 | bool[] rotorLow = new bool[rotorBlocksX.Count]; // True when power is way to low | |
142 | bool[] rotorFineTune = new bool[rotorBlocksX.Count]; // Fine Tune, very slow rotor | |
143 | bool[] reverse = new bool[rotorBlocksX.Count]; // Reverse rotor | |
144 | bool[] rotorOnY = new bool[rotorBlocksY.Count]; // Stop or start rotor | |
145 | ||
146 | bool containsFalse = false; // Dynamic timer management. Increase or decrease timer | |
147 | float pwr = 0f, lastPwr = 0f; // Current and last power reading | |
148 | ||
149 | if (EnableOxygenMode) { | |
150 | for(int i = 0; i < solarBlocks.Count; i++) { // For each oxygen farm... | |
151 | var solar = solarBlocks[i] as IMyOxygenFarm; // Support for oxygen farm | |
152 | if(solar != null) { // Yes I am | |
153 | GetOxygen(solar, ref pwr); // Get oxygen level, return into existing pwr variable | |
154 | lastPwr = GetAndSetLastOxygen(solar, pwr); // Get and set last runs oxygen level | |
155 | reverse[i] = (lastPwr < pwr || pwr == 0) ? false : true; // Change rotor direction | |
156 | rotorOn[i] = (pwr <= HighPwrValue) ? true : false; // Turn on rotor | |
157 | rotorLow[i] = (pwr < HighPwrValue/2) ? true : false; // Slow or fast rotor | |
158 | rotorFineTune[i] = (pwr > HighPwrValue*10/11) ? true : false; // Fine tune rotor | |
159 | } | |
160 | } | |
161 | } else { | |
162 | for(int i = 0; i < solarBlocks.Count; i++) { // For each solar panel... | |
163 | var solar = solarBlocks[i] as IMySolarPanel; // I am a Solar Panel | |
164 | if(solar != null) { // Yes I am | |
165 | GetPower(solar, ref pwr); // Get Power from solar panel, return into existing pwr variable | |
166 | lastPwr = GetAndSetLastPwr(solar, pwr); // Get and set last runs power | |
167 | reverse[i] = (lastPwr < pwr || pwr == 0) ? false : true; // Change rotor direction | |
168 | rotorOn[i] = (pwr <= HighPwrValue) ? true : false; // Turn on rotor | |
169 | rotorLow[i] = (pwr < HighPwrValue/2) ? true : false; // Slow or fast rotor | |
170 | rotorFineTune[i] = (pwr > HighPwrValue*10/11) ? true : false; // Fine tune rotor | |
171 | if (lastPwr == 0 && pwr == 0 && AutoNightMode || Me.TerminalRunArgument == "Nightmode") { // TEST | |
172 | nightmode = true; | |
173 | } else { | |
174 | nightmode = false; | |
175 | } | |
176 | } | |
177 | } | |
178 | } | |
179 | if (nightmode) { | |
180 | Echo ("Night mode."); | |
181 | // Do other stuff | |
182 | } | |
183 | ||
184 | for(int i = 0; i < rotorBlocksX.Count; i++) { // For each rotorX... | |
185 | IMyMotorStator rotor = rotorBlocksX[i] as IMyMotorStator; // I am a Rotor | |
186 | if(rotor != null) { // Yes I am | |
187 | if (ForceAutoTorque) { // Force torque. | |
188 | rotor.BrakingTorque = float.MaxValue; | |
189 | rotor.Torque = 30000000f; | |
190 | } | |
191 | if (nightmode) { | |
192 | TriggerRotor(rotor, false, false, ref containsFalse); // Stop rotor | |
193 | } else { | |
194 | SetRotorSpeed(rotor, rotorLow[i], rotorFineTune[i]); // Dynamic rotor speed | |
195 | if (!rotorOn[i]) { // Turn off... | |
196 | TriggerRotor(rotor, false, false, ref containsFalse); // Stop rotor | |
197 | } else if (rotorOn[i] && EnableTwoAxisMode && reverse[i]) { // Turn On, dual axis mode, and reverse | |
198 | rotorOnY[i] = CheckAndUpdateRotorName(rotor); | |
199 | if (rotorOnY[i]) { | |
200 | TriggerRotor(rotor, false, false, ref containsFalse); // Y on, therefore X off. | |
201 | } | |
202 | ||
203 | if (!rotorOnY[i]) { | |
204 | TriggerRotor(rotor, true, reverse[i], ref containsFalse); // Y off, therefore X on, and reverse. | |
205 | } | |
206 | } else if (rotorOn[i] && EnableTwoAxisMode && !reverse[i]) { | |
207 | rotorOnY[i] = CheckAndUpdateRotorName(rotor); | |
208 | if (!rotorOnY[i]) { | |
209 | TriggerRotor(rotor, true, reverse[i], ref containsFalse); // Start rotor. Reverse if needed | |
210 | } | |
211 | } else { | |
212 | TriggerRotor(rotor, true, reverse[i], ref containsFalse); // Start rotor. Reverse if needed | |
213 | } | |
214 | } | |
215 | } | |
216 | } | |
217 | ||
218 | if (EnableTwoAxisMode) { | |
219 | for(int i = 0; i < rotorBlocksY.Count; i++) { // For each rotorY... | |
220 | IMyMotorStator rotor = rotorBlocksY[i] as IMyMotorStator; // I am a Rotor | |
221 | if(rotor != null) { // Yes I am | |
222 | if (ForceAutoTorque) { | |
223 | rotor.BrakingTorque = float.MaxValue; | |
224 | rotor.Torque = 30000000f; | |
225 | } | |
226 | if (nightmode) { | |
227 | TriggerRotor(rotor, false, false, ref containsFalse); // Stop rotor | |
228 | } else { | |
229 | if (rotorOnY[i] == true) { | |
230 | SetRotorSpeed(rotor, rotorLow[i], rotorFineTune[i]); // Dynamic rotor speed | |
231 | TriggerRotor(rotor, true, reverse[i], ref containsFalse); // Start rotor Y. Reverse if needed | |
232 | } else { | |
233 | TriggerRotor(rotor, false, false, ref containsFalse); | |
234 | } | |
235 | } | |
236 | } | |
237 | } | |
238 | } | |
239 | ||
240 | // Duplicate stuff | |
241 | if (EnableDuplicateRotor3 && !containsFalse) { // Main rotors aligned, lets duplicate changes... | |
242 | RotorDuplicate(NameOfDuplicateSource3, SearchForDuplicateDest3, InverseDuplicateRotor3); | |
243 | } | |
244 | if (EnableDuplicateRotor2 && !containsFalse) { // Main rotors aligned, lets duplicate changes... | |
245 | RotorDuplicate(NameOfDuplicateSource2, SearchForDuplicateDest2, InverseDuplicateRotor2); | |
246 | } | |
247 | if (EnableDuplicateRotor1 && !containsFalse) { // Main rotors aligned, lets duplicate changes... | |
248 | RotorDuplicate(NameOfDuplicateSource1, SearchForDuplicateDest1, InverseDuplicateRotor1); | |
249 | } | |
250 | if (!containsFalse && ControlCustomTimerAtMaxPower) { // Main rotors aligned, lets control a timer | |
251 | delayCount = 0; | |
252 | ControlTimer(timer2); | |
253 | } | |
254 | if (containsFalse && ControlCustomTimerAtLowPower){ | |
255 | delayCount++; | |
256 | if (delayCount == 5) { | |
257 | delayCount = 0; | |
258 | ControlTimer(timer3); | |
259 | } | |
260 | } | |
261 | if (EnableLCD) PrintToLCD(textToLCD); | |
262 | } | |
263 | ||
264 | #endregion | |
265 | #region Methods | |
266 | ||
267 | void PrintToLCD(string text) { | |
268 | if (EnableLCD) { | |
269 | for(int i = 0; i < LcdBlocks.Count; i++) { // For each LCD... | |
270 | IMyTextPanel LCD = LcdBlocks[i] as IMyTextPanel; | |
271 | LCD.WritePublicText(text, false); | |
272 | LCD.SetValue("FontSize", 0.9f); | |
273 | LCD.ShowPublicTextOnScreen(); | |
274 | } | |
275 | } | |
276 | } | |
277 | void ControlTimer(IMyTimerBlock thistimer) { | |
278 | thistimer.ApplyAction("TriggerNow"); | |
279 | } | |
280 | ||
281 | void TriggerRotor(IMyMotorStator rotor, bool state, bool reverse, ref bool containsFalse) { | |
282 | if (!state) { | |
283 | rotor.ApplyAction("OnOff_Off"); // Stop rotor | |
284 | } else { | |
285 | rotor.ApplyAction("OnOff_On"); // Start rotor | |
286 | containsFalse = true; | |
287 | if (reverse) { rotor.ApplyAction("Reverse"); } | |
288 | } | |
289 | } | |
290 | ||
291 | void GetPower(IMySolarPanel solar, ref float pwr) { | |
292 | pwr = solar.MaxOutput * 1000; | |
293 | float test = pwr / HighPwrValue * 100f; | |
294 | decimal test2 = Math.Round((decimal)test); | |
295 | textToLCD += pwr.ToString() + " kWh (" + test2.ToString() + " %)\n"; | |
296 | Echo(pwr.ToString() + " kWh"); | |
297 | } | |
298 | ||
299 | void GetOxygen(IMyOxygenFarm solar, ref float pwr) { | |
300 | pwr = solar.GetOutput()* 1.8f; | |
301 | Echo(pwr.ToString() + " L/min"); | |
302 | } | |
303 | ||
304 | float RotorPosition(IMyMotorStator Rotor) { | |
305 | if (Rotor == null) { | |
306 | Echo( "Rotor not found. Returning 0"); | |
307 | return 0; | |
308 | } | |
309 | return (float)Math.Round(Rotor.Angle * (180.0 / Math.PI), 0); | |
310 | } | |
311 | ||
312 | void RotorDuplicate(string SourceRotorName, string RotorName, bool Inverse) { | |
313 | GridTerminalSystem.SearchBlocksOfName(SourceRotorName, SourceRotors); // Search for Solar Source Blocks | |
314 | IMyMotorStator SourceRotor = SourceRotors[0] as IMyMotorStator; // I am a Rotor | |
315 | float SetPosition = RotorPosition(SourceRotor); | |
316 | GridTerminalSystem.SearchBlocksOfName(RotorName, Rotors); // Search for Solar Blocks | |
317 | if (Rotors.Count == 0) { | |
318 | Echo("Cannot find any duplicate destination rotors."); | |
319 | return; | |
320 | } | |
321 | if (Inverse) { | |
322 | SetPosition = -SetPosition; | |
323 | } | |
324 | for(int i = 0; i < Rotors.Count; i++) { // For each rotor... | |
325 | IMyMotorStator Rotor = Rotors[i] as IMyMotorStator; // I am a Rotor | |
326 | float DestPosition = RotorPosition(Rotor); | |
327 | if (ForceAutoTorque) { | |
328 | Rotor.BrakingTorque = float.MaxValue; | |
329 | Rotor.Torque = 10000000f; | |
330 | } | |
331 | Rotor.SetValueFloat("LowerLimit",SetPosition); | |
332 | Rotor.SetValueFloat("UpperLimit",SetPosition); | |
333 | textToLCD += "Duplication #" + i + ": "; | |
334 | if (SetPosition == DestPosition) { | |
335 | Rotor.TargetVelocityRPM = 0f; | |
336 | Rotor.ApplyAction("OnOff_Off"); // Stop rotor | |
337 | textToLCD += "Locked\n"; | |
338 | } else if (SetPosition < DestPosition) { | |
339 | Rotor.ApplyAction("OnOff_On"); // Start rotor | |
340 | Rotor.TargetVelocityRPM = -rotorspeed; | |
341 | textToLCD += "Running\n"; | |
342 | } else if (SetPosition > DestPosition) { | |
343 | Rotor.ApplyAction("OnOff_On"); // Start rotor | |
344 | Rotor.TargetVelocityRPM = rotorspeed; | |
345 | textToLCD += "Running\n"; | |
346 | } | |
347 | } | |
348 | } | |
349 | ||
350 | void SetRotorSpeed(IMyMotorStator rotor, bool fast, bool FineTune) { | |
351 | float SetTo = 0f; | |
352 | bool VelocityPositive = (rotor.TargetVelocityRPM > 0f) ? true : false; // True if positive velocity | |
353 | if (fast) { // Under half of required power from solar panel. Will increase speed | |
354 | SetTo = (VelocityPositive) ? rotorspeed : -rotorspeed; | |
355 | } else if (FineTune) { // Fine tune speed | |
356 | SetTo = (VelocityPositive) ? rotorspeed/3.7f : -rotorspeed/3.7f; | |
357 | } else { // Not far from required power. Lower speed for increased accuracy | |
358 | SetTo = (VelocityPositive) ? rotorspeed/1.7f : -rotorspeed/1.7f; | |
359 | } | |
360 | rotor.TargetVelocityRPM = SetTo; | |
361 | } | |
362 | ||
363 | float GetAndSetLastPwr(IMySolarPanel solar, float CurrentPower) { | |
364 | float OldPwr = 0f; | |
365 | string[] words = solar.CustomName.Split(':'); // Colon split words | |
366 | if (words.Length > 1) { // If there is data after colon | |
367 | if (!float.TryParse(words[1], out OldPwr)) { OldPwr = 0f;} // Try to get data into float variable | |
368 | } | |
369 | solar.CustomName = (words[0] + ":" + CurrentPower); // Set current power in solar panel name | |
370 | return OldPwr; | |
371 | } | |
372 | float GetAndSetLastOxygen(IMyOxygenFarm solar, float CurrentPower) { | |
373 | float OldPwr = 0f; | |
374 | string[] words = solar.CustomName.Split(':'); // Colon split words | |
375 | if (words.Length > 1) { // If there is data after colon | |
376 | if (!float.TryParse(words[1], out OldPwr)) { OldPwr = 0f;} // Try to get data into float variable | |
377 | } | |
378 | solar.CustomName = (words[0] + ":" + CurrentPower); // Set current power in solar panel name | |
379 | return OldPwr; | |
380 | } | |
381 | ||
382 | bool CheckAndUpdateRotorName(IMyMotorStator rotor) { | |
383 | int OldCount = 0; | |
384 | string[] words = rotor.CustomName.Split(':'); // Colon split words | |
385 | if (words.Length > 1) { // If there is data after colon | |
386 | if (!int.TryParse(words[1], out OldCount)) { OldCount = 0;} // Try to get data into int variable | |
387 | } | |
388 | int NewCount = OldCount + 1; | |
389 | if (OldCount > 6) { | |
390 | rotor.CustomName = (words[0] + ":0"); | |
391 | return true; | |
392 | } | |
393 | else if (OldCount >= 3) { | |
394 | rotor.CustomName = (words[0] + ":" + NewCount); // Set current count in rotor name | |
395 | return true; | |
396 | } else { | |
397 | rotor.CustomName = (words[0] + ":" + NewCount); // Set current count in rotor name | |
398 | return false; | |
399 | } | |
400 | } | |
401 | #endregion |