1. This forum is obsolete and read-only. Feel free to contact us at support.keenswh.com

Cargo Capacity Script help

Discussion in 'Programming Questions and Suggestions' started by Morphik, Mar 17, 2015.

Thread Status:
This last post in this thread was made more than 31 days old.
  1. Morphik

    Morphik Apprentice Engineer

    Messages:
    186
    I don't remember where I got this script, I am sure its not on the workshop. I think I grabbed it off someones blueprint they had on the workshop.
    This script works but I want to try to edit it to do something else. Right now it will display a Capacity bar on a LCD for one container and it works on the grid each container can have its own LCD. What I want is to change the code to do is show a Overall percentage for a set of containers with the starting name. I know there are other scripts out there that do this, but none display it in a vertical column like this one does. And I am still a beginner script writer, I can read and understand what everything is and does. I just can't seem to write or reconfigure it the way I want.


    Code:
        void Main() 
    { 
        
        List<IMyTerminalBlock> grid = new List<IMyTerminalBlock>();
     
        //search for BU batteries 
        grid.Clear(); 
        GridTerminalSystem.GetBlocksOfType<IMyCargoContainer>(grid);
        IMyTextPanel lcd;
        float calc;  
        string  displaytext;
        string nameofcontainer;
        string[] tmp;
        int index = 0;
        
        string lcdstart = "Cap LCD "; //k2
        string lcdwarning = "LCD Warning";
        string containerstart = "Cargo ";
        //
        for (int k = 0 ; k < grid.Count ; k++) {
            if (grid[k].CustomName.StartsWith(containerstart)) {
                    index++;
                    calc = ((float)grid[k].GetInventory(0).CurrentVolume /  (float)grid[k].GetInventory(0).MaxVolume) * 100;
                    calc = calc;
                    //Affichage
                    lcd = GridTerminalSystem.GetBlockWithName(lcdstart + (index)) as IMyTextPanel;
                    if (lcd != null) {
                        tmp = grid[k].CustomName.Split(':');
                        nameofcontainer = tmp[0];
                        displaytext = "Capacity \n" + nameofcontainer + " : " + (int)calc + "%\n";
                        for (int x = 0 ; x <= 10 ; x++) {
                            if (calc >= 100 - x * 10) {
                                displaytext += "    |    -----    |\n";
                            } else {
                                displaytext += "    |              |\n";
                            }
                        }
                        lcd.ShowTextureOnScreen(); //pb affichage 
                        lcd.WritePublicText(displaytext, false); 
                        lcd.ShowPublicTextOnScreen();
                        //WARNING SIGN
                        lcd = GridTerminalSystem.GetBlockWithName(lcdwarning) as IMyTextPanel;
                        if (lcd != null) {
                            if (calc > 80) {
                                lcd.GetActionWithName("OnOff_On").Apply(lcd);
                            } else {  
                                lcd.GetActionWithName("OnOff_Off").Apply(lcd);  
                            }
                        } 
                    }
                }   
            } 
    }
    
     
  2. indigodarkwolf

    indigodarkwolf Apprentice Engineer

    Messages:
    115
    This builds in Visual Studio, but I haven't tested it in-game. Does this seem like what you're going for? Or are you looking for a script that updates a bunch of different LCDs with individual cargo cans' capacities?
    Code:
    void Main()
    {
      // What the cargo cans' custom names need to start with.
      string containerStart = "Cargo ";
    
      // What the graph LCD's custom name needs to be.
      string lcdName = "Cap LCD";
    
      // What the warning LCD's custom name needs to be.
      string warningName = "LCD Warning";
    
      // Get a list of all the cargo cans starting with containerstart.
      // The second argument to GetBlocksOfType() is called a lambda. It's like a function, but doesn't have a name.
      // Google "c# lambda" for more information.
      List<IMyTerminalBlock> grid = new List<IMyTerminalBlock>();
      GridTerminalSystem.GetBlocksOfType<IMyCargoContainer>(grid, b => b.CustomName.StartsWith(containerStart));
    
      // The total amount of space used
      float usedVolume = 0.0f;
    
      // The total capacity of the cargo cans
      float maxVolume = 0.0f;
    
      // Count the total space used and the total capacity of the cargo cans
      // The "foreach()" control structure doesn't work, but there is a member of the List class called "ForEach()" which does a similar job.
      // I'm using a lambda again, it's just a little different because it doesn't return a value like the one above.
      grid.ForEach(b => {
        usedVolume += (float)b.GetInventory(0).CurrentVolume;
        maxVolume += (float)b.GetInventory(0).MaxVolume;
      });
    
      // The percentage of space used.
      float pctUsed = 100.0f * usedVolume / maxVolume;
    
      // Find the LCD we'll use for the summary graph
      // "var" is just a shortcut for "I don't want to spell out the name of the type, because it's obvious. The computer can figure it out."
      var lcd = GridTerminalSystem.GetBlockWithName(lcdName) as IMyTextPanel;
    
      // If we found that LCD, let's write the graph to it.
      if (lcd != null) {
        // Start the display text with a title and our space used in %.
        string displayText = String.Format("Capacity\nOverall: {0}%\n", (int)pctUsed);
        // Build the graph from the top down.
        for (int x = 0; x <= 10; x++) {
          if (pctUsed >= 100 - x * 10) {
            displayText += "    |    -----    |\n";
          } else {
            displayText += "    |              |\n";
          }
        }
        // Show the result on the LCD.
        lcd.ShowTextureOnScreen();
        lcd.WritePublicText(displayText, false);
        lcd.ShowPublicTextOnScreen();
      }
    
      // Now let's find the warning sign.
      lcd = GridTerminalSystem.GetBlockWithName(warningName) as IMyTextPanel;
    
      // If we found the warning sign, decide whether to have it turned on or off.
      if (lcd != null) {
        // If we've used more than 80% of our cargo, have it turn on. Else, have it turn off.
        if (pctUsed > 80) {
          lcd.ApplyAction("OnOff_On");
        } else {
          lcd.ApplyAction("OnOff_Off");
        }
      }
    }
     
    Last edited by a moderator: Mar 17, 2015
  3. Morphik

    Morphik Apprentice Engineer

    Messages:
    186
    That is exactly what I was trying to do, and your revised code works. Thanks for writing one up and putting a little tutorial in with it. Great help!

    BTW the original already showed individual cargo containers on individual LCD's I was looking for a Overall like the code you provided.
     
  4. Morphik

    Morphik Apprentice Engineer

    Messages:
    186
    Actually I had the brilliant idea to change it from looking for specific names to look for a group so the containers would be easier to add to expand the coverage of the script.
    this is the code I have and I am having exceptions at lines 17 and 19 in the ForEach.
    I am not sure if I am heading in the right direction or not.
    Code:
    void Main() 
    { 
          string lcdName = "Cap Ingot"; 
        
          var cargos = GetBlocksFromGroup("Ingots");
          var lcd = GridTerminalSystem.GetBlockWithName(lcdName) as IMyTextPanel; 
        
        for (int i = 0; i< cargos.Count; i++) 
              { 
                var cargo = cargos[i] as IMyCargoContainer; 
                float usedVolume = 0.0f; 
                float maxVolume = 0.0f; 
                
            cargo.ForEach(
                      usedVolume += (float)cargo.GetInventory(0).CurrentVolume; 
                      maxVolume += (float)cargo.GetInventory(0).MaxVolume;
            )
     
                float pctUsed = 100.0f * usedVolume / maxVolume; 
                if (lcd != null) { 
                      string displayText = String.Format("Capacity\nOverall: {0}%\n", (int)pctUsed); 
                      for (int x = 0; x <= 10; x++) { 
                            if (pctUsed >= 100 - x * 10) { 
                                  displayText += "    |    -----    |\n"; 
                                }         else { 
                                  displayText += "    |              |\n";
                            } 
                      } 
                } 
            lcd.ShowTextureOnScreen(); 
            lcd.WritePublicText(displayText, false); 
            lcd.ShowPublicTextOnScreen(); 
          } 
    }
    
     
  5. MisterSwift

    MisterSwift Apprentice Engineer

    Messages:
    367
    Don't forget to add your semi-colon ; after the closing bracket ) of the ForEach().

    Also, maybe I'm missing something, but you're running the ForEach method on the single instance of cargo and not on the list of all of them? cargo vs cargos

    In indigodarkwolf's example they're running the ForEach on the List and not using a for(int i...) loop. But in your cope you're already looping through all the IMyTerminalBlock in "cargos" using a for loop I think?

    GetBlocksFromGroup() is a custom function, there's no builtin method that lets you pass a name as parameter and returns the list of blocks that I know of, so you need to provide the code for that

    Note: also don't declare float usedVolume and maxVolume inside the for loop or they'll be reset back to 0 for each count of i

    I've written up some quick code that should work
     
    Last edited by a moderator: Mar 17, 2015
  6. MisterSwift

    MisterSwift Apprentice Engineer

    Messages:
    367
    I haven't checked if this compiles, but it's worth a shot:
    Code:
    void Main() {
    // STEP 1: Declare and get the blocks we're working with.
       string lcdName = "Cap Ingot";
       string blockGroupName = "Ingots";
    
       // Declare a list that will store all our cargo container blocks
       List<IMyTerminalBlock> cargos = new List<IMyTerminalBlock();
    
       // Calls a custom function named GetBlocksFromGroup() that will fill this list
       cargos = GetBlocksFromGroup(blockGroupName);
    
       // Set the variable "lcd" to a reference to our LCD/Text panel
       IMyTerminalBlock lcd = GridTerminalSystem.GetBlockWithName(lcdName);
    
       // Do some basic error checking. 
       if (lcd == null | cargos == null) return null; // Something wasn't found
       if (cargos.Count == 0) return null; // Block group has no blocks in it
    
    
    // STEP 2: Loop through each cargo container and make a total of the volumes
       // Start searching through all the blocks, and getting their current and max volumes
       // Assumes that all these blocks are cargo containers! Will not work if a block
       // with no inventories is in the block group you've called
       float totalCurrentVolume = 0.00f;
       float totalMaxVolume = 0.00f;
       float percentUsed = 0.00f;
    
       // Loops through each item in the List<IMyTerminalBlock> named "cargos"
       // adding their used/max capacity to the running total
       cargos.ForEach(x => {
          totalCurrentVolume += (float)x.GetInventory(0).CurrentVolume;
          totalMaxVolume += (float)x.GetInventory(0).MaxVolume;
       });  
    
       // Now that we have the totals, we can calculate the percentage
       percentUsed = totalCurrentVolume / totalMaxVolume;
    
    
    // STEP 3: Build a string with all the results
       // Notes: \n inserts a new line, the {0} says to insert the first numerical parameter, {1} the next etc.
       // {0:N0} says to format that numbers a number with 0 decimal places. {0:N2} would be 2 decimal places
       // {0:P0} says to format the number as a percent (and multiplies it by 100 for you)
       string outputText = "";
       outputText += "CAPACITY:\n";
       outputText += String.Format("{0} cargo container(s) found.\n", cargos.Count);
       outputText += String.Format("Volume: {0:N0}/{1:N0}\n", totalCurrentvolume, totalMaxVolume);
       outputText += String.Format("Usage: {0:P0}\n", percentUsed);
       outputText += drawVerticalBar(percentUsed); // Calls a custom function to draw the bar
    
    
    // STEP 4: Actually print the results to the screen
       lcd.ApplyAction("OnOff_On");            // Make sure the screen is on
       lcd.ShowTextureOnScreen();              // Toggle it to textures
       lcd.WritePublicText(outputText, false); // Set the text on the screen
       lcd.ShowPublicTextOnScreen();           // Toggle text back on
    
    // FINISHED!
    } // end of void Main()
    
     
    
    // A custom function that returns a string with line breaks drawing a vertical
    // progress bar
    string drawVerticalBar(float value, int height = 10) {
       string result = "";
       int n = (int)value * height; // How many filled rows there are
    
       // Count backwards from the heighest line back down to line 1
       for (int j = height; j > 0; j--) {
          if (j > n) { 
             outputText += "   |            |\n"; 
          } else {
             outputText += "   |   ------   |\n";
          }
       }
    
       return result;
    } // drawVerticalBar() ends here
    
     
    
    // A custom function for returning a list of all the blocks in a block group.
    // Parameter: Exact name of the block group
    // Returns:   A list containing all the blocks in the block group
    //            Returns null if block group not found.
    List<IMyTerminalBlock> GetBlocksFromGroup(string groupName) {
       // Initialise a variable that can hold a list of multiple IMyBlockGroup
       List<IMyBlockGroup> blockGroups = new List<IMyBlockGroup>();
    
       // Populate it with all the block groups on this grid
       blockGroups = GridTerminalSystem.BlockGroups;
    
       // Loop through all of them. If the name of the block group matches 
       // the one we are looking for, return its contents, which are a list
       // of all the blocks in the block group
       for (int i = 0; i < blockGroups.Count; i++) {
          if (blockGroups[i].Name == groupName) return blockGroups[i].Blocks;
       }
    
       // If we can't find a block group with the name, return null
       return null;   
    } GetBlocksFromGroup() ends here
    
     
  7. indigodarkwolf

    indigodarkwolf Apprentice Engineer

    Messages:
    115
    @Morphik: The reason you're getting an exception is that IMyCargoContainer does not have a ForEach() member function. After all, why would it need one? It's a single IMyCargoContainer, unlike List<IMyCargoContainer>, which is a list of several IMyCargoContainers, or List<IMyTerminalBlock>, which is a list of many IMyTerminalBlocks.

    Well, okay, a List doesn't always have to contain more than one of something. But it can contain more than one, which is why it's useful for it to have a ForEach() member.

    I think maybe I confused you by using ForEach(), and lambdas. These are tools I've become very accustomed to, because they get around some pretty horrific limits that K# has. (K# is what I call the in-game scripting, because it's been butchered pretty badly and it's too aggravating for me to think of it as C#).

    Let me try again, posting my original script without ForEach() and lambdas.
    Code:
    void Main(){
      // What the cargo cans' custom names need to start with.
      string containerStart = "Cargo ";
     
      // What the graph LCD's custom name needs to be.
      string lcdName = "Cap LCD";
     
      // What the warning LCD's custom name needs to be.
      string warningName = "LCD Warning";
     
      // First, get all cargo containers.
      List<IMyTerminalBlock> grid = new List<IMyTerminalBlock>();
      GridTerminalSystem.GetBlocksOfType<IMyCargoContainer>(grid);
     
      // This list will hold all the cargo containers we actually want.
      List<IMyTerminalBlock> blocks = new List<IMyTerminalBlock>();
      
      // Go through all the cargo containers and pick the ones we want.
      for(int i=0; i<grid.Count; i++) {
        if(grid[i].CustomName.StartsWith(start)) {
          blocks.Add(grid[i]);
        }
      }
     
      // The total amount of space used
      float usedVolume = 0.0f;
     
      // The total capacity of the cargo cans
      float maxVolume = 0.0f;
     
      // Count the total space used and the total capacity of the cargo cans
      for(int i=0; i<blocks.Count; i++) {
        var b = blocks[b];
        usedVolume += (float)b.GetInventory(0).CurrentVolume;
        maxVolume += (float)b.GetInventory(0).MaxVolume;
      }
     
      // The percentage of space used.
      float pctUsed = 100.0f * usedVolume / maxVolume;
     
      // Find the LCD we'll use for the summary graph
      // "var" is just a shortcut for "I don't want to spell out the name of the type, because it's obvious. The computer can figure it out."
      var lcd = GridTerminalSystem.GetBlockWithName(lcdName) as IMyTextPanel;
     
      // If we found that LCD, let's write the graph to it.
      if (lcd != null) {
        // Start the display text with a title and our space used in %.
        string displayText = String.Format("Capacity\nOverall: {0}%\n", (int)pctUsed);
        // Build the graph from the top down.
        for (int x = 0; x <= 10; x++) {
          if (pctUsed >= 100 - x * 10) {
            displayText += "    |    -----    |\n";
          } else {
            displayText += "    |              |\n";
          }
        }
        // Show the result on the LCD.
        lcd.ShowTextureOnScreen();
        lcd.WritePublicText(displayText, false);
        lcd.ShowPublicTextOnScreen();
      }
     
      // Now let's find the warning sign.
      lcd = GridTerminalSystem.GetBlockWithName(warningName) as IMyTextPanel;
     
      // If we found the warning sign, decide whether to have it turned on or off.
      if (lcd != null) {
        // If we've used more than 80% of our cargo, have it turn on. Else, have it turn off.
        if (pctUsed > 80) {
          lcd.ApplyAction("OnOff_On");
        } else {
          lcd.ApplyAction("OnOff_Off");
        }
      }
    }
    From there, we can make a change to the script so it grabs blocks from a group, as opposed to grabbing them by name from the big global list. See if you can spot the changes:
    Code:
    void Main()
    {
      // What the cargo cans' custom names need to start with.
      string groupName = "Ingots";
     
      // What the graph LCD's custom name needs to be.
      string lcdName = "Cap LCD";
     
      // What the warning LCD's custom name needs to be.
      string warningName = "LCD Warning";
     
      // I don't want to type GridTerminalSystem.BlockGroups a lot, so let's save it to a variable.
      var groups = GridTerminalSystem.BlockGroups;
      
      // This will be the group we actually want. We don't have a value to start it off with, so we set it to "null".
      IMyBlockGroup myGroup = null;
      
      // Find the group we want.
      for(int i=0; i<groups.Count; i++) {
        if(groups[i].Name == groupName) {
          myGroup = groups[i];
        }
      }
      
      // This list will hold all the cargo containers we actually want.
      List<IMyTerminalBlock> blocks = myGroup.Blocks;
      
      // The total amount of space used
      float usedVolume = 0.0f;
     
      // The total capacity of the cargo cans
      float maxVolume = 0.0f;
     
      // Count the total space used and the total capacity of the cargo cans
      for(int i=0; i<blocks.Count; i++) {
        var b = blocks[i];
        usedVolume += (float)b.GetInventory(0).CurrentVolume;
        maxVolume += (float)b.GetInventory(0).MaxVolume;
      }
     
      // The percentage of space used.
      float pctUsed = 100.0f * usedVolume / maxVolume;
     
      // Find the LCD we'll use for the summary graph
      // "var" is just a shortcut for "I don't want to spell out the name of the type, because it's obvious. The computer can figure it out."
      var lcd = GridTerminalSystem.GetBlockWithName(lcdName) as IMyTextPanel;
     
      // If we found that LCD, let's write the graph to it.
      if (lcd != null) {
        // Start the display text with a title and our space used in %.
        string displayText = String.Format("Capacity\nOverall: {0}%\n", (int)pctUsed);
        // Build the graph from the top down.
        for (int x = 0; x <= 10; x++) {
          if (pctUsed >= 100 - x * 10) {
            displayText += "    |    -----    |\n";
          } else {
            displayText += "    |              |\n";
          }
        }
        // Show the result on the LCD.
        lcd.ShowTextureOnScreen();
        lcd.WritePublicText(displayText, false);
        lcd.ShowPublicTextOnScreen();
      }
     
      // Now let's find the warning sign.
      lcd = GridTerminalSystem.GetBlockWithName(warningName) as IMyTextPanel;
     
      // If we found the warning sign, decide whether to have it turned on or off.
      if (lcd != null) {
        // If we've used more than 80% of our cargo, have it turn on. Else, have it turn off.
        if (pctUsed > 80) {
          lcd.ApplyAction("OnOff_On");
        } else {
          lcd.ApplyAction("OnOff_Off");
        }
      }
    }
     
    Last edited by a moderator: Mar 19, 2015
  8. Morphik

    Morphik Apprentice Engineer

    Messages:
    186
    I guess I need to study more, or perhaps my brain isn't wired for programming. Its weird for me I can read the code and understand mostly the structure, but when it comes to writing one I fumble. It's like walking in your house in pitch dark, you know the layout of your house but you can still take that wrong turn and stump your toe.
    Thanks for the patience with me though, I know how frustrating it is teaching someone something. I am learning new stuff each day.
     
  9. MisterSwift

    MisterSwift Apprentice Engineer

    Messages:
    367
    It feels really rewarding when you finally get your code to work though, doesn't it? :D

    I enjoy looking through other people's code to see how they went around solving problems and doing things. No one ever does things exactly the same and it's nice to pick up new ways of doing things
     
  10. Morphik

    Morphik Apprentice Engineer

    Messages:
    186
    MisterSwift and IndigoDarkWolf, thank you both for your help. Sorry for the long reply time, I was feeling sick all day yesterday and stayed off the net. I tested both of your solutions to the Group container scripts and unfortunately neither work. I will try to see what I can figure out and see if I can get either to work.
    Indigodarkwolf - Your's gets this error list.
    Line 29: The name 'grid' does not exist in the current context
    line 30: The name 'grid' does not exist in the current context
    line 30: The name 'start' does not exist in the current context
    line 31: The name 'grid' does not exist in the current context
    line 43: Cannot use local variable 'b' before it is declared.

    This seems easy enough for me to clean up, I will see what I can do.

    MisterSwift - Your code gets
    Line 203: Method must have return type
    Line 203: ; expected
    Line 203: ; expected
    Not sure what it is but line 203 is this
    Code:
    } GetBlocksFromGroup() ends here
    
    I don't think this was supposed to be here but if I change it I get a whole new set of errors.
    Line 13: Syntax error, '>' expected
    Line 141: The name 'outputText' does not exist in the current context
    Line 145: The name 'outputText' does not exist in the current context
     
  11. indigodarkwolf

    indigodarkwolf Apprentice Engineer

    Messages:
    115
    Ah, whoops. I should have checked my changes to see if they'd compile first. And that block of code that spun through "grid" should have been deleted entirely. I've edited mine to fix the compile errors, hopefully it works now.
     
  12. Morphik

    Morphik Apprentice Engineer

    Messages:
    186
    Indigodarkwolf, you code works really well. Sorry I forgot to come back and tell you half a month ago :p

    Anyways seeing as this is all your work, I was going to post the script to the workshop, if that is okay with you. I will credit you with 99.99% of the work :D
     
  13. indigodarkwolf

    indigodarkwolf Apprentice Engineer

    Messages:
    115
    Meh. No attribution required. Anything I post in these forums can safely be considered CC0. Having useful bits of scripting out in the world is its own reward.

    Besides, it's not all my code, and I view the parts that are as little more than a cantrip. If you want to see real wizardry, check out that BARABAS script. Very slick.
     
  14. Morphik

    Morphik Apprentice Engineer

    Messages:
    186
    I don't know what happened Indigo. A moderator came in and edited your code's and now they don't work. so anyone reading this will copy your code and it not function.

    It might have happened on the forum move.

    Edit: It must have happened on the move, on everyone's posted code before the move every instance of "<" has been changed too "&lt"
     
    Last edited: Apr 12, 2015
Thread Status:
This last post in this thread was made more than 31 days old.