diff --git a/DrillingRig/DrillingRig.cs b/DrillingRig/DrillingRig.cs index 183c1d9..14e29aa 100644 --- a/DrillingRig/DrillingRig.cs +++ b/DrillingRig/DrillingRig.cs @@ -1,4 +1,4 @@ -/** DrillingRig.cs v. 0.31 +/** DrillingRig.cs v. 0.4 Drilling program for Space Engineers. Author: mkazin @@ -8,15 +8,22 @@ **/ // Number of times a rotor will swing in an arc across the current distance -// Has a linear relationship with rotor RPM. +// Generally has a linear relationship with rotor RPM. Can be reduced by using +// a multi-drill head. // Do not lower below two, which allows for distance piston to retract. // Setting your rotor at above 2RPM will increase the chances of the drill arm getting stuck. -const int MAX_ROTOR_SWINGS = 2; +// You can probably get away with 4 RPM, though at this speed you should be monitoring the drill. +// At 5 RPM this script has proven to fail with a cross-shaped drill head (I'm thinking +// of attaching a rotor to the drill head and maybe trying a square-shaped drill head +// to see if that saves me). +const int MAX_ROTOR_SWINGS = 3; // Amount to increse the distance piston after we finish our swings in meters. -const float DISTANCE_DELTA = 2.0f; +// The wider the drill head on the distance axis, the bigger you can make this. +// Best to use the quotient of a the length of the piston divided by an integer to avoid remainders. +const float DISTANCE_DELTA = 2.5f; -// Amount to raise drill pistons/lower lift pistons between swing sets. +// Amount to extend drill pistons/retract lift pistons between swing sets to lower the drill head. const float HEIGHT_DELTA = 0.4f; // Constants which differ per drill @@ -43,6 +50,20 @@ const string NAME_LCD = "Drill - LCD"; /**/ + +/*** New Cobalt Mine **** +private static List NAME_DRILL_PISTONS = new List() { + "Drill Piston - 1st", + "Drill Piston - 2nd"}; +private static List NAME_LIFT_PISTONS = new List() { + "Drill - Lift Piston - Lower", + "Drill - Lift Piston - Upper" }; +const string NAME_DISTANCE_PISTON = "Drill - Distance Piston"; +const string NAME_ROTOR = "Drill - Advanced Rotor"; +const string NAME_LCD = "Drill - LCD"; +/**/ + + /*** Money Pit (gold & silver) **** private static List NAME_DRILL_PISTONS = new List() { "Drill Piston - 1st", @@ -57,6 +78,22 @@ const string NAME_LCD = "Drill LCD"; /**/ + +//*** Money Pit Redix (more gold & silver) **** +private static List NAME_DRILL_PISTONS = new List() { + "Drill Piston - 1st", + "Drill Piston - 2nd", + "Drill Piston - 3rd", + "Drill Piston - 4th", + "Drill Piston - 5th" }; +private static List NAME_LIFT_PISTONS = new List() { + "Lift Piston - Lower", + "Lift Piston - Upper" }; +const string NAME_DISTANCE_PISTON = "Distance Piston"; +const string NAME_ROTOR = "Drill Rotor"; +const string NAME_LCD = "Drill LCD"; +/**/ + /*** Silver Drill (HQ) *** private static List NAME_DRILL_PISTONS = new List() { "Silver Drill - Lower Drill Piston", @@ -92,7 +129,7 @@ const string NAME_LCD = "Drill B - LCD"; /**/ -//*** Drill C *** +/*** Drill C *** private static List NAME_DRILL_PISTONS = new List() { "Drill C - Drill Piston - 1st", "Drill C - Drill Piston - 2nd", @@ -118,8 +155,11 @@ const string NAME_ROTOR = "Iron Drill - Advanced Rotor"; /**/ +// How many times the rotor's direction has been reversed at the current distance/angle settings +int motorSwing = 1; -int motorSwing = 0; // How many times the rotor's direction has been reversed at the current distance/angle settings +// Direction the rotor is moving. 1 = clockwise, -1 = counter-clockwise (positive/negative sign of RPM). +float rotorDirection = 1; // List of pistons used to adjust height of the drill. Does NOT include the distance piston. List drillPistons = new List(); @@ -162,6 +202,7 @@ public Program() distancePiston = GridTerminalSystem.GetBlockWithName(NAME_DISTANCE_PISTON) as IMyPistonBase; rotor = GridTerminalSystem.GetBlockWithName(NAME_ROTOR) as IMyMotorAdvancedStator; + rotorDirection = rotor.TargetVelocityRPM < 0.0f ? -1.0f : 1.0f; lcd = GridTerminalSystem.GetBlockWithName(NAME_LCD) as IMyTextSurface; ClearLCD(); @@ -173,17 +214,20 @@ public Program() public void Save() { string data = ""; - data += String.Format("{0:.##}", distancePiston.CurrentPosition) + DELIMITER_GROUP; + data += String.Format("{0:0.##}", distancePiston.CurrentPosition) + DELIMITER_GROUP; foreach(IMyPistonBase piston in liftPistons) { - data += String.Format("{0:.##}", piston.CurrentPosition) + DELIMITER_ITEM; + data += String.Format("{0:0.##}", piston.CurrentPosition) + DELIMITER_ITEM; } data += DELIMITER_GROUP; foreach(IMyPistonBase piston in drillPistons) { - data += String.Format("{0:.##}", piston.CurrentPosition) + DELIMITER_ITEM; + data += String.Format("{0:0.##}", piston.CurrentPosition) + DELIMITER_ITEM; } + data += DELIMITER_GROUP; + data += haltAndCatchFire; + Storage = data; } @@ -198,33 +242,46 @@ public bool Load() { Output("Storage: " + Storage); string[] storedData = Storage.Split(DELIMITER_GROUP); if (storedData.Length == 0) { - Output("Invalid data in Storage"); + Output("Invalid data in Storage - Missing delimeters"); return false; } - float value; - if (! float.TryParse(storedData[0], out value)) { - Output("Invalid data in Storage"); - return false; - } - distancePiston.MaxLimit = value; - try { + // Backward-compatiblity support for <= 0.32 which stored empty strings for pistons at 0.0 + distancePiston.MaxLimit = storedData[0].Length == 0 ? 0.0f : float.Parse(storedData[0]); + Output("Loaded " + distancePiston.CustomName + " : [[" + distancePiston.MaxLimit + "]]"); + string[] liftPistonData = storedData[1].Split(DELIMITER_ITEM); for (int index=0; index < liftPistonData.Length - 1; index++) { - liftPistons[index].MinLimit = float.Parse(liftPistonData[index]); + // Backward-compatiblity support for <= 0.32 which stored empty strings for pistons at 0.0 + liftPistons[index].MinLimit = liftPistonData[index].Length == 0 ? 0.0f : float.Parse(liftPistonData[index]); + Output("Loaded " + liftPistons[index].CustomName + " : [[" + liftPistons[index].MinLimit + "]]"); } string[] drillPistonData = storedData[2].Split(DELIMITER_ITEM); for (int index=0; index < drillPistonData.Length - 1; index++) { - drillPistons[index].MaxLimit = float.Parse(drillPistonData[index]); + // Backward-compatiblity support for <= 0.32 which stored empty strings for pistons at 0.0 + drillPistons[index].MaxLimit = drillPistonData[index].Length == 0 ? 0.0f : float.Parse(drillPistonData[index]); + Output("Loaded " + drillPistons[index].CustomName + " : [[" + drillPistons[index].MaxLimit + "]]"); } } catch { - Output("Invalid data in Storage"); + Output("Invalid data in Storage. Resetting it."); Storage = ""; return false; } + // Check if we stored a value for haltAndCatchFire + // (to remain backward-compatible with versions <= 0.32) + if (storedData.Length >= 4) { + haltAndCatchFire = bool.Parse(storedData[3]); + if (haltAndCatchFire) { + // Note: this will be our only output. + Output("Drilling rig completed program using current parameters."); + } + } else { + haltAndCatchFire = false; + } + return true; } @@ -252,13 +309,8 @@ public void resetPistons() { inPistonRetract = true; } -public void Main() +public void Main(string argument, UpdateType updateSource) { - // if (initialSetup) { - // resetPistons(); - // initialSetup = false; - // } - if (haltAndCatchFire) { return; } @@ -280,17 +332,20 @@ public void Main() } } - if (rotorStopped()) { - rotor.TargetVelocityRPM = -rotor.TargetVelocityRPM; + if (rotorAtMax()) { + // TargetVelocityRPM is unreliable at higher velocities due to bounce-back, + // so we need to track and set clockwise/counterclockwise direction. + rotorDirection *= -1.0f; + rotor.TargetVelocityRPM = rotorDirection * Math.Abs(rotor.TargetVelocityRPM); Output("Reversed rotor"); - Output("Velocity set to:" + rotor.TargetVelocityRPM + " RPM"); + Output("Velocity set to:" + formatToTwo(rotor.TargetVelocityRPM) + " RPM"); if (motorSwing < MAX_ROTOR_SWINGS) { motorSwing += 1; Output("Starting swing #" + motorSwing); } else { Output("Completed all swings (" + motorSwing + ")"); - motorSwing = 0; + motorSwing = 1; if (distancePiston.MaxLimit == distancePiston.HighestPosition) { @@ -298,9 +353,10 @@ public void Main() distancePiston.MaxLimit = distancePiston.LowestPosition; if (! lengthenNextPiston()) { - Output("No pistons left to extend. Reversing all pistons"); + Output("No pistons left to extend. Resetting all pistons and ending program."); inPistonRetract = true; reverseAllPistons(); + haltAndCatchFire = true; } Save(); @@ -310,41 +366,63 @@ public void Main() } else { distancePiston.MaxLimit = Math.Min(distancePiston.HighestPosition, distancePiston.MaxLimit + DISTANCE_DELTA); - Output("Setting Distance Piston's MaxLimit to:" + distancePiston.MaxLimit); + Output("Extending distance piston to:" + formatToTwo(distancePiston.MaxLimit)); // Make sure the distance piston will extend distancePiston.Velocity = Math.Abs(distancePiston.Velocity); } } } else { // Output current status - Output("In swing #" + motorSwing); - Output(distancePiston.CustomName + " at " + distancePiston.CurrentPosition); - foreach (IMyPistonBase piston in drillPistons) { - Output(piston.CustomName + " at " + piston.CurrentPosition); - } - foreach (IMyPistonBase piston in liftPistons) { - Output(piston.CustomName + " at " + piston.CurrentPosition); - } - - Output("Storage: " + Storage); + Output("Swing #" + motorSwing + " of " + MAX_ROTOR_SWINGS + " at " + formatToTwo(rotor.TargetVelocityRPM) + " RPM"); } -} -public bool isDrillPiston(IMyPistonBase piston) { - return piston.CustomName.Contains(PREFIX_DRILL_PISTON); -} + Output(distancePiston.CustomName + " at " + formatToTwo(distancePiston.CurrentPosition)); + foreach (IMyPistonBase piston in drillPistons) { + Output(piston.CustomName + " at " + formatToTwo(piston.CurrentPosition)); + } + foreach (IMyPistonBase piston in liftPistons) { + Output(piston.CustomName + " at " + formatToTwo(piston.CurrentPosition)); + } -public bool isLiftPiston(IMyPistonBase piston) { - return piston.CustomName.Contains(PREFIX_LIFT_PISTON); + Output("Storage: " + Storage); } - -public bool rotorStopped() { - - Output("Rotor Angle @ " + rotor.Angle); - Output("Range: " + rotor.LowerLimitRad + " -> " + rotor.UpperLimitRad); - if (rotor.Angle <= rotor.LowerLimitRad || - rotor.Angle >= rotor.UpperLimitRad) { +List RPM_WARNINGS = new List() { + "Rotor velocity (RPM) not set.", // < .5 + "", "", "", "", "", // 0.5 .. 2.5 + "You're going to bang up your drills.", // 3 + "Say goodbye to your warranty.", // 3.5 + "That's really not a good idea", // 4 + "I see you aim to misbehave.", // 4.5 + "At this velocity you should expect\nyour drill to get stuck in the rock.\nOr fall off.", // 5 + "Beware the wrath of Klang." }; // 5.5 + +// Helper function to determine if rotor is at its current target angle (or close enough) +// We calculate a margin within which the rotor is considered to have completed a swing in degrees. +// At higher rotor velocities the rotor will bounce back, which seriously slowed down +// the swing duration time as the rotor would keep trying to precisely hit the target +// at the time of the UpdateFrequency. This margin avoids the need to update faster +// (which would use more CPU cycles) +public bool rotorAtMax() { + // Formula for calculating the margin is: (360 degrees * RPM) / 60 seconds + // (I'm assuming 100 ticks is a second for simplicity's sake) + float margin = 6.0f * Math.Abs(rotor.TargetVelocityRPM); + if (margin > 15.0f) { + Output("Warning: RPM too high!"); + Output(RPM_WARNINGS[(int)Math.Round(Math.Abs(rotor.TargetVelocityRPM * 2.0f))]); + Output("Margin set to safety default."); + margin = 20.0f; + } + float angle = radiansToDegrees(rotor.Angle); + Output("Rotor Angle @ " + formatToTwo(angle) + " degrees"); + Output("Range: " + formatToTwo(radiansToDegrees(rotor.LowerLimitRad)) + " <-> " + formatToTwo(radiansToDegrees(rotor.UpperLimitRad)) + " degrees"); + Output("Margin: " + formatToTwo(margin)); + Output("Reversing at: " + formatToTwo(angle) + (rotorDirection < 0 ? + " < " + formatToTwo(radiansToDegrees(rotor.LowerLimitRad) + margin) : + " >= " + formatToTwo(radiansToDegrees(rotor.UpperLimitRad) - margin))); + + if ((rotorDirection < 0 && angle <= radiansToDegrees(rotor.LowerLimitRad) + margin) || + (rotorDirection > 0 && angle >= radiansToDegrees(rotor.UpperLimitRad) - margin)) { return true; } return false; @@ -400,7 +478,6 @@ public bool lengthenNextPiston() { } } Output("Drill at lowest point. Returning false."); - haltAndCatchFire = true; return false; } @@ -425,8 +502,12 @@ private void Output(string text) { private void ClearLCD() { screenText = new StringBuilder(); } - - +private float radiansToDegrees(float radians) { + return radians * (180.0f / (float)Math.PI); +} +private String formatToTwo(float number) { + return String.Format("{0:0.##}", number); +} /** Important SDK information: diff --git a/DrillingRig/DrillingRig.md b/DrillingRig/DrillingRig.md index fd2322f..1813a1a 100644 --- a/DrillingRig/DrillingRig.md +++ b/DrillingRig/DrillingRig.md @@ -7,17 +7,17 @@ Use it to quickly plop down a drilling rig connected to a refinery and some stor ## Rigs explained -The drill rig is composed of five sections. To see how these pieces connect, observe the four arrows in the below screenshot. +The drilling rig is composed of five sections. To see how these pieces connect, observe the four arrows in the below screenshot. Listed in build order: -- An Advanced Rotor at the base, used to swing the drill in an arc. (see red arrow in screenshot) +- Advanced Rotor at the base, used to swing the drill in an arc. (see red arrow in screenshot) Notes: (1) Use Advanced Rotor, as the regular rotor will not convey drilled materials. (2) Leave room under it to connect a conveyer. Placing it atop a conveyer junction is best.) -- m * Lift Pistons, attached to the rotor, facing upward. As the name implies, these raise the rest of the rig. +- Lift Arm, composed of m * Lift Pistons, attached to the rotor, facing upward. As the name implies, these raise the rest of the rig. - A Distance Piston, connected to the top Lift Piston via conveyor junction or curved conveyer. This will move the drill outward, providing our second dimension. (light grey arrow is pointing at where it should be) Note: I've been using a single distance as I expect adding a second will cause the rig to fall apart. This will require some experimentation. -- n * Drill Pistons, similarly connected to the attachment of the Distance piston, facing downward- our third dimension. The more drill pistons, the deeper you can go. (yellow arrow) -- A drill, attached to the lowest Drill Piston (see tip of the hand drill) +- Drill Arm, n * Drill Pistons, similarly connected to the attachment of the Distance piston, facing downward- our third dimension. The more drill pistons, the deeper you can go. (yellow arrow) +- Drill Head, attached to the lowest Drill Piston (see tip of the hand drill). See drill heads section. The dark grey arrow is pointing at the material which this rig has removed. A great big happy smile. @@ -30,21 +30,55 @@ Setting the rotor's minimum and maximum angle allows you control over the two bo Except for the rotor component, a demonstration of this type of rig can be seen in [part #5 of Splitsie's excelent tutorial series](https://www.youtube.com/watch?v=knRgN0WhzKg&list=PLfMGCUepUcNzLePdu3dZfMTLfWq1bclUK&index=5). ## Usage: -* Modify constants in NAME_VERTICAL_PISTONS to the names of your blocks +* Modify constants in NAME_DRILL_PISTONS, NAME_LIFT_PISTONS, NAME_DISTANCE_PISTON, NAME_ROTOR to the names of your blocks +* Optional - if you want to use an LCD screen, also modify NAME_LCD to match your block * Modify the PREFIX_ constants to match text in your blocks (you may need to rename your blocks) * Enter your modified code a programmable cube. -* Program your rotor with upper and lower angle bounds; move the rotor to within those bounds. +* Program your rotor with upper and lower angle bounds; move the rotor to within those bounds. Set the RPM (ideally no higher than 2.0) +* Set the desired velocity on your pistons (up to 0.3 should avoid punching the ground) * Turn on the programmable cube. +## Tips: +* On high-gravity bodies you'll want to have drills be as perpendicular to gravity as possible. This is to avoid angering Klang by adding unnecessary complexity to your rig. An extended distance piston could become a lever and counter your rotor's torque. +* Limit your Lift Arm to two pistons. I've seen catastrophic failures caused by a third. +* To avoid excessive sway you may get better results by using "shared inertia" on the lift arm as well as separately the drill arm. Select all the pistons on an arm and then press the "shared inertia" button. + ## Future Development: * Tune default values for swings, velocities (either hard-code, or perhaps dynamically using m & n). -* Auto-detect drilling rigs? (easy when a single rig is on the current grid, might be able to use subgrids off a rotor) +** Later on auto-tune the rig. +* Auto-detect drilling rigs? (easy when a single rig is on the current grid, might be able to use subgrids off a rotor, or follow Isy's method of using groups) * Support multiple Distance Pistons? (Would such a construct even be stable?) -* Output? How about calculating an ETA? Swing count, Piston status? +* Ore output? How about calculating an ETA? * Theoretically this program should support small grid drilling rigs, but this has not yet been tested. +* Damage detection and safety shutdown. If you have any ideas or feature requests, I'd love to hear them. +## Drill Heads +I started out with a single drill attached directly to the lowest drill piston. That worked fine, but was slow. + +Here are some variants I tried or want to try: + +### Cross-shaped drills +A junction is attached to the lowest drill piston, with drills hanging off Curved Conveyor Tubes. + +With upgrades added in v0.40 and a cross-shaped drill-head I managed to get a rig (2 Lift/5 Drill) running fairly well at 4 RPM. The conveyers failed at 5 RPM. + +Possible improvement: remove the conveyer tubes. Either attach the drills directly to the junction, or replace the tubes with junctions. + +### Square-shaped drills +Untested. Similar to cross-shaped drills, but drills are affixed to the corners of the 3x3 box surrounding the piston. + +### Rotating drill head +Untested. Hypothesis: avoids the problem of a drill missing a section due to speed and hitting it later on. Not sure how fast I'd need to rotate the advanced rotor. + +### If I can't build a perfect drill head... +#### Damage detection +Damage will probably creep in. If I ever get to the point that material stops flowing from a drill, it can be considered too damaged to continue (the extra weight will throw off the rotor; also there's limited storage anyway and we don't want to lose precious metals). The reason for looking at material is because parts other than the drill tend to get damaged (piston tops aren't quite rugged enough for the rigors of a rig). If material stops flowing, hit the emergency shut down (lock the rotor and turn off programmable block - in that order) + +#### Self-repairing +Untested. Just a crazy idea I might try. If I can't avoid damage, maybe I can mitigate it. None of the parts are too expensive if refining the stone. + ## Extra credit: Geometry lesson ### Q: What shape is the area we are going to be drilling? diff --git a/README.md b/README.md index 5e0304d..7f24f10 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # SpaceEngineers Collection of scripts written for SpaceEngineers -## ![DrillingRig](https://github.com/mkazin/SpaceEngineers/DrillingRig/) +## [DrillingRig](DrillingRig/DrillingRig.md) Automation of a drilling rig composed of an advanced rotor, lift pistons, a distance piston, and drill pistons.