Jump to content

Optimising storage space in a facility - (Moved)


Recommended Posts

Hi,

This will be my very first post on the Autoit forums.I hope I posted this in the right place, my apologies if it is not.

I am a technician for an automatic storage facility, recently we have grown so big that I hardly get around my electrical and mechnical maintenance. Over the passed years I have been spending more and more time behind the computer arranging the storage  so that its optimised for free space. These are very repetitive tasks with a lot of factors to take care off. The company that installed the storage facility has given us functions to optimise space but they hardly work properly and do not take enough parameters in account. So I am giving it a go myself. In the past I have used Autoit to automate things at home of low importance however all the programming I know is hometaught.


So far I have a program that extracts an array, the array is checked on any faults during the extraction and checked if its the proper data. From there on I would like to arrange the pallets in the physical storage(there are short rows that hold 2~6 pallets and long rows  that hold 9 pallets). The smallest storage rows should be filled up first  with the least number of movements of pallets .

So for this project I would like to know if I write my code properly , tips and remarks are very welcome.
I am uncertain if I wrote this properly and if not how I should write it .

Func _Find_Optimisable_rows(Const ByRef $a_Block)
    ;Declaration
    ;$a_Block[rows][properties] = an imported array of storage rows with each their own number of pallets and total space 
    ;$a_block[row][0] = number of pallets stored in the storage row
    ;$a_block[row][1] = maximum storage spaces in this row
    Local $Total_space = 0
    Local $Number_of_pallets = 0
    Local $a_Rows_to_be_filled[UBound($a_Block, 1)]; declare an onedimensional array with same number of indices as the imported array
    ;/declaration

    For $i = 0 To UBound($a_Block, 1) - 1 ; for all rows in array $a_block
        $Total_space += $a_Block[$i][1] ; add the current row's total space to the total space
        $Number_of_pallets += $a_Block[$i][0] ; add current row's number of pallets to total number of pallets
    Next

    _Sort_Block($a_Block)
    ; sort the array on maximum storage spaces, smallest first 
    ;secondary sort all the rows with same number of maximum storage space by number of pallets stored with the biggest number of pallets first

    For $i = 0 To UBound($a_Block, 1) - 1 ; for all rows in array $a_block
        If ($Total_space - $Number_of_pallets) > $a_Block[$i][1] And $Number_of_pallets > 0 Then;check if we can free up space by filling this row/check for end-of-loop
            $a_Rows_to_be_filled[$i] = 1; set the local array so we know the row can be completly filled
            $Total_space -= $a_Block[$i][1] ; substract the filled row from maximum storage space
            $Number_of_pallets -= $a_Block[$i][1] ; subtract the number of pallets we used to fill that row
        Else
            $a_Rows_to_be_filled[$i] = 0
            ReDim $a_Rows_to_be_filled[$i] ; shrinks array to needed size
            Return $a_Rows_to_be_filled ; end function and return the array with rows that can be optimised
        EndIf
    Next    
EndFunc

 

 

Link to comment
Share on other sites

  • Moderators

Moved to the appropriate forum, as the Developer General Discussion forum very clearly states:

Quote

General development and scripting discussions. If it's super geeky and you don't know where to put it - it's probably here.


Do not create AutoIt-related topics here, use the AutoIt General Help and Support or AutoIt Technical Discussion forums.

Moderation Team

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Link to comment
Share on other sites

Ok, time to try a bump and some more information.

I am a technician for an automatic storage facility, recently we have grown so big that I hardly get around my electrical and mechnical maintenance. Over the passed years I have been spending more and more time behind the computer arranging the storage  so that its optimised for free space. These are very repetitive tasks with a lot of factors to take care off. The company that installed the storage facility has given us functions to optimise space but they hardly work properly and do not take enough parameters in account. So I am giving it a go myself. In the past I have used Autoit to automate things at home of low importance however all the programming I know is hometaught.

the automatic warehous is divided up in several blocks (up to 18)

each block is maximum 10 rows high, maximum 102 rows far.

each row can contain x number of euro pallets, x number of industry pallets  or x number of wooden boxes.

there are huge cranes driving between blocks that extract and store pallets, the cranes will never place different items together.

depending on supply and demand (certain clients ask for specific pallets, others just want entire runs of a product) the cranes will retrievethe product without taking in account of optimalisation.

this means after a while there are plenty of rows that each have 2~3 pallets while there are other rows with the same article  with also only 2~3 pallets.

We have short rows (due to space limitations) and long rows. the short rows are blocked for automatic filling up because we prefer to place batches of a product as close together as possible. From time to time we are supposed to place all articles together  that can be placed together  and the rows that are not full , if possible, we place them in the short rows

then for every 2 blocks (for every crane) I have a list of rows used (kanalenInW2, W2 is block 5 to 8)

and for the same locations I have a list with items .

I have written a program that finds all buttons/info on the programs available to us.

it extracts the lists and places them in a 3d array such that each row is the first and second dimension and the information about the items inside the row is the third dimension.

each item has its expiration date and the eldest can be placed behind new ones but never vica versa (there is a FIFO limitation of 5 days, however this is item specific and should be referenced to from another list)

so far the output I get is a list of possible rows that I can place together and I can use my logic to place them together but if I have for example

the rows are labelled as xx.yyy.zz with x being the block, y being the row of the block, and z being the height

 

 

so an example

in 03.002.05 there are 4 industry pallets with article X, max space there for industry pallets is  8 (euro is smallest, industry pallets slightly bigger and boxes the biggest)

in 03.016.01 there are 2 industry pallets with article X, max space there is 6 industry pallets

we know that we should try in short channels and they will fit in, so we place them together in 03.016.01.

however some rows only have 1 or 2 spaces, some have 4 or 5 , standard is 9 but we have some that go up to 14. freeing up long channels is a major goal and if its not possible to free up long channels  we put non-full long rows into short ones. however we cannot treat the short rows lightly because we have over 1000 different items possible stored. so there are a lot of rows that are not completly filled. these belong in short rows.Every new item that is different gets a new row, and automatically it will search for a long row already containing those if none are found it will place it in a new long one. eventually there is a long row that is not completly filled and we place it in short rows then.

my attempt currently is just checks for all the items with same article number , some are already manually placed in short rows , some are in long rows by automatic storage. it does not use new short rows and that is fine. but if there are already items in a short row and we can free up a long row by filling up the short one it should do that.

 

how do I translate all of the above into code? without leaving out certain parameters?

I would first check to free up long rows by placing items together  in short rows IF there is already an item in the short row.

then when it is done with ALL of that I would have it place the long rows that are not completly filled in free short rows

this is my attempt  any ideas? I am on vacation now and would like to work this out before I return.

Func _Find_Optimisable_rows(Const ByRef $a_Block)
    ;Declaration
    ;$a_Block[rows][properties] = an imported array of storage rows with each their own number of pallets and total space 
    ;$a_block[row][0] = number of pallets stored in the storage row
    ;$a_block[row][1] = maximum storage spaces in this row
    Local $Total_space = 0
    Local $Number_of_pallets = 0
    Local $a_Rows_to_be_filled[UBound($a_Block, 1)]; declare an onedimensional array with same number of indices as the imported array
    ;/declaration

    For $i = 0 To UBound($a_Block, 1) - 1 ; for all rows in array $a_block
        $Total_space += $a_Block[$i][1] ; add the current row's total space to the total space
        $Number_of_pallets += $a_Block[$i][0] ; add current row's number of pallets to total number of pallets
    Next

    _Sort_Block($a_Block)
    ; sort the array on maximum storage spaces, smallest first 
    ;secondary sort all the rows with same number of maximum storage space by number of pallets stored with the biggest number of pallets first

    For $i = 0 To UBound($a_Block, 1) - 1 ; for all rows in array $a_block
        If ($Total_space - $Number_of_pallets) > $a_Block[$i][1] And $Number_of_pallets > 0 Then;check if we can free up space by filling this row/check for end-of-loop
            $a_Rows_to_be_filled[$i] = 1; set the local array so we know the row can be completly filled
            $Total_space -= $a_Block[$i][1] ; substract the filled row from maximum storage space
            $Number_of_pallets -= $a_Block[$i][1] ; subtract the number of pallets we used to fill that row
        Else
            $a_Rows_to_be_filled[$i] = 0
            ReDim $a_Rows_to_be_filled[$i] ; shrinks array to needed size
            Return $a_Rows_to_be_filled ; end function and return the array with rows that can be optimised
        EndIf
    Next    
EndFunc

edit: there is A mistake with ReDim $a_Rows_to_be_filled[$i]

it should be : ReDim $a_Rows_to_be_filled[$i+1]

 

representation.jpg

example.jpg

KanalenInW2.xlsx

LEInW2.xlsx

Edited by ternal
Link to comment
Share on other sites

tagging in @czardas as this screams to be treated as 2 3D arrays rather than (6) 7x18 2D arrays on the left and (8) 8x18 2D arrays on the right.  I am headed that route but i travel infintely slower than he does.

 

 

 

 

,-. .--. ________ .-. .-. ,---. ,-. .-. .-. .-.
|(| / /\ \ |\ /| |__ __||| | | || .-' | |/ / \ \_/ )/
(_) / /__\ \ |(\ / | )| | | `-' | | `-. | | / __ \ (_)
| | | __ | (_)\/ | (_) | | .-. | | .-' | | \ |__| ) (
| | | | |)| | \ / | | | | | |)| | `--. | |) \ | |
`-' |_| (_) | |\/| | `-' /( (_)/( __.' |((_)-' /(_|
'-' '-' (__) (__) (_) (__)

Link to comment
Share on other sites

I actually treat the entire place as 1 3D array, the rows are labelled as XX , YYY,ZZ xx being the block, YYY being the rows , ZZ being the elevation/height.

the location, xx.yyy.zz is just one column in both the first excel file and the second. I use the location to merge them into a 3d array

the different blocks are just in the same array.
the short rows are not bound to any block. in each block we have long and short rows. some block mostly have short rows and others mostly long rows but its kinda mixed

we also do not move from block 1 to 9 for example. but I extract the lists in the blocks they are allowed to transfer

Edited by ternal
Link to comment
Share on other sites

Is there more code (there are no 3D references)? That optimization of a nearest neighbor solution seems hindered by slicing it into 2D sections in the function (but I have been wronger before)

 and you have criteria that it be placed into the shorter & nearest rows first?  So if you were building an If..Then, you would want to check that entire left side first, no?

Edited by iamtheky

,-. .--. ________ .-. .-. ,---. ,-. .-. .-. .-.
|(| / /\ \ |\ /| |__ __||| | | || .-' | |/ / \ \_/ )/
(_) / /__\ \ |(\ / | )| | | `-' | | `-. | | / __ \ (_)
| | | __ | (_)\/ | (_) | | .-. | | .-' | | \ |__| ) (
| | | | |)| | \ / | | | | | |)| | `--. | |) \ | |
`-' |_| (_) | |\/| | `-' /( (_)/( __.' |((_)-' /(_|
'-' '-' (__) (__) (_) (__)

Link to comment
Share on other sites

I use the first page of the 3d array since I placed the highest and lowest expiration date on the first page. but there are different things in there I will need to adress in the future like weight and how often it is retrieved from the warehouse. (heaviest low and lighest items on top for safety and mechanical wear).

I added slices of the 3d array .

the first page of that array would be what I start with in my example.

LE is items so #LE is number of items in a row. thats on the first page (0) from there on forward each page is one slice of the all the rows in the first page (each page contains its own item and just one item, index [0] contains the info on the row

 

edit: the left side is a simple representation. as I stated before maybe 90% of those rows are short, but where it was possible they made long ones.

so 10% will be long. my script goes through all rows and checks the space (the space is also dependant on the item, because they have different sizes so it extracts and calculates the max space for current item already(max plaatsen). it also retrieves number of items inside each row(#LE) and free space in that channel (vrije plaatsen in kanaal), then we have article(artikel) the height of the row ( boxes are not high so we have different heights as well in rows, for extra headache?) fifo gebied is how many days they may differ from each other , then we have a few random stuff not of importance now. THT is experiation date

3D array.jpg

 

currently my script only looks to optimise rows that have at least 1 item in them. And we will start with that. I have enough resources at my disposal (3 pc's with I7's and SSD's ) to work everything out.

its not a nearest neighbour issue rather then finding out how it should move them.

 

moving everything into short rows will clog up the short rows and in no time we will have long rows that are not completly filled.

so it should only fill up  the already used short rows (for now at least) when it can make a long row free else it should just place the remainder in the short row

 

my script right now checks if a row can be freed.

when its done with that I would have to restart, take away the shortest row and run it again, then the shortest after that etc etc.

then I have all the options I can to move it around . and it should pick one of those options depending on the length of the channels that are freed

 

 

but I feel like its going to be more work to explain all of this properly then to write something myself. lol

just wanted to know how a programmer should start tackling such a problem.
I have a technical background, electronics(learned crude basics of programming there), took extra courses in welding on request of my boss, took courses in programming PLC's on request of my boss. however I did not ever receive training as how to program such algorithms

 

I would upload my entire script but its about 1600 lines long, mostly written with dutch or german variable names and comments and I know how it works but it does get confusing sometimes... especially since it covers a lot more then we are talking now and most of the functions acces the same arrays and I did not take any effort in keeping that tidy :/ my apologies

Some parts of it are not even used anymore as it was a work in progress, still is. some variables get declared but never used and then a new variable with a very very similar name jumps in and I continue on that one.

I should rewrite the entire thing tbh

Edited by ternal
Link to comment
Share on other sites

Many parameters (dimensions) + multiple, partly conflicting constraints: use Simulated Annealing. Study the TSP example (corresponds to the distances to move pallets/boxes), then think long and hard about how to define your cost function, depending on what's relatively most important (you may have to spend some time testing and tuning this for "best" results).

Link to comment
Share on other sites

Oooh nice - a 3D queuing system. This is one of the most interesting questions I've encountered. As @RTFC says, raw brute force may not be suitable. Rearranging the pallets must be done with the fewest number of moves within localized regions. I don't fully understand the stacking order: I assume older pallets ought to be placed on top (for quick access). Unfortunately I don't have a ready made solution, but this is a very complex problem which requires some study. The pallets should only be moved once within a single reshuffle (presumably unstacked and restacked). Need to reread this question a few times.

Edited by czardas
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...