Microsoft hotfix indexer and browser

A while back I made a quick hotfix indexer script and database browser.

These were not too great but had sufficient functionality for the time. Now that I have expanded on functionality I want to write this as a single post.

Any Microsoft enterprise technician could see the value in having a list like this.
The question is why Microsoft don’t provide this list themselves.
Anyhow, let’s begin!

This package contains three files. One indexer.ps1. One HotfixBrowser.ps1 and a config file where you specify SQL connectionstring and table to be used before running the scripts.

The heart of the script is the indexer. It goes through a range specified and lists them in a database. The table must have the following columns:

Capture2

Other columns can be added if needed and will be listed in the hotfix browser.
What I do is add a scheduled task that runs this script with a set of ranges every weekend to get a up-to date view.

When the indexer has completed you can start HotfixBrowser.ps1browser

From here you can search with standard SQL syntax. All fields to a LIKE search so for example All windows 8 hotfixes released during 2014 has this search criteria.
2

Clicking search allows you to quickly see the short text of all articles involving Windows 8 with a hotfix download available allowing you to work efficiently and determine if any of the hotfixes are applicable to your client environment.
3

There are also the option to go ‘advanced’ which allows you to edit the Select query as you see fit.
4

In the File menu you can export the current datatable as a CSV file.
In the tools mode you can save and load searches as well as choose visible columns and enable edit mode.
Edit mode makes the cells writable and allows you to send a UPDATE query to the server (given the appropriate rights) to edit some fields such as note and other custom fields.

Project: 3D Printer from scratch. Part 5 – Trial and error. And finally success!

With all the mechanical parts assembled all that is left is firmware calibration – or so we thought.

With all basic calibration done we noticed on prints that the X and Y axises were shifting. as shown below. Looking around and asking on communities this seems often to be due to when the stepper motors skips steps or the timing belt slipping.
We tried everything suggested, from cooling stepper drivers, motors, tightening belts and turning down acceleration in the firmware but nothing worked.

It was first when we loosened the belts and ran the motors we noticed that the pulleys were slipping off the shafts. This was because the pulleys screws were biting into the brass pipe which we used to bridge the gap between our motors and the pulleys did not provide enough friction. When we loosened the belts even less friction was applied between the brass and drive shaft and the slippage became apparent.IMAG1013 IMAG1012 IMAG1011 IMAG1020   IMAG1022 IMAG1023 IMAG1024

After buying some metal epoxy to fixate everything together we are able to finally produce perfectly straight prints!

IMAG1025
The print above are some holders for a spool where I designed the feet myself.
It’s quite an amazing feeling to draw something in a 3D program one day and to hold it the next.
IMAG1027

Project: 3D Printer from scratch. Part 4 – Assembly

With all the parts delivered it was time to assemble.

I’m not going to go through the whole build process. But it consisted of loads of screwing around (pun intended) and It’s not the prettiest build but i honestly don’t care about looks. It’s function I’m after.

To make a long story short here are some pictures of the build process:
IMG_0729
IMG_0730 IMG_0714 IMG_0715 IMG_0716 IMG_0717 IMG_0718

IMG_0719

IMG_0720

IMG_0721

IMG_0722
IMG_0724 IMG_0725 IMG_0726 IMG_0727 IMG_0728

IMG_0733

IMG_0732

One issue we had was that the motors shaft diameter was 4mm and the pulleys had 5mm. We solved this by buying a brass pipe with 4mm inner and 5mm outer diameter.
We then cut the pipe and screwed them on. This kept the pulleys from rotating in an elipse and gave us a nice smooth circular and even rotation.

Not being the most experianced DIY guys we spent about half a billion hours on assembly. But in the end it worked and we managed to produce our first ever print!

Success!!!
IMG_0755

Hotfix browser

#Requires -version 2
[CmdletBinding()]
Param($min,$max)
Begin{
    $MyPath = Split-Path $MyInvocation.MyCommand.path -Parent
    
    Function SaveFileDialog{
    Param(
        [Parameter(Mandatory=$True)]
        $Filetype
    )
        [Void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
        $SaveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
        $SaveFileDialog.Filter = "$Filetype files (*.$Filetype)|*.$Filetype|All files (*.*)|*.*"
        $status = $SaveFileDialog.ShowDialog()

        If($status -eq "Cancel"){$Return = $status}
        Else{$Return = $SaveFileDialog.FileName}

        $SaveFileDialog.Dispose()
        Return $Return
    }
    
    Function Call-Form{
        [reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null
        [reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null
        $EditMode = $False
        $savepath = "$MyPath\SavedSearches.xml"
        
        ### MAINFORM
        $form1 = New-Object System.Windows.Forms.Form
        $label7 = New-Object System.Windows.Forms.Label
        $label6 = New-Object System.Windows.Forms.Label
        $NotesSearch = New-Object System.Windows.Forms.TextBox
        $LinkSearch = New-Object System.Windows.Forms.TextBox
        $label5 = New-Object System.Windows.Forms.Label
        $label4 = New-Object System.Windows.Forms.Label
        $Progressbar = New-Object System.Windows.Forms.ProgressBar
        $DownloadSearch = New-Object System.Windows.Forms.ComboBox
        $label3 = New-Object System.Windows.Forms.Label
        $label2 = New-Object System.Windows.Forms.Label
        $label1 = New-Object System.Windows.Forms.Label
        $ReviewSearch = New-Object System.Windows.Forms.TextBox
        $AppliestoSearch = New-Object System.Windows.Forms.TextBox
        $ShortTextSearch = New-Object System.Windows.Forms.TextBox
        $KBIDsearch = New-Object System.Windows.Forms.TextBox
        $searchButton = New-Object System.Windows.Forms.Button
        $dataGrid = New-Object System.Windows.Forms.DataGridView
        $InitialFormWindowState = New-Object System.Windows.Forms.FormWindowState
        

        #SAVE FORM
        $saveForm = New-Object System.Windows.Forms.Form
        $saveBox = New-Object System.Windows.Forms.TextBox
        $SaveButton = New-Object System.Windows.Forms.Button
        $listView = New-Object System.Windows.Forms.ListView
        
        #LOAD FORM
        $loadForm = New-Object System.Windows.Forms.Form
        $loadBox = New-Object System.Windows.Forms.TextBox
        $loadButton = New-Object System.Windows.Forms.Button
        
        $searchButton_Click ={
            $Progressbar.Show()
            If($DownloadSearch.Text -eq "True"){
                $DL = "1"
            }
            Else{
                $DL = "0"
            }
            
            
            #Get values from fields
            $SearchValues = @{}
            $SearchValues.Add("KBID",$KBIDsearch.Text)
            $SearchValues.Add("ShortText",$ShortTextSearch.Text)
            $SearchValues.Add("Download",$DL)
            $SearchValues.Add("AppliesTo",$AppliestoSearch.Text)
            $SearchValues.Add("LastReviewed",$ReviewSearch.Text)
            $SearchValues.Add("Link",$LinkSearch.Text)
            $SearchValues.Add("Notes",$NotesSearch.Text)
            
            #Compile search query
            $Query = "Select KBID,ShortText,Download,AppliesTo,LastReviewed,Link,Notes FROM hotfix_primary WHERE Exist = 1"
            $SearchValues.Keys | ForEach-Object{
                If($SearchValues.$_ -ne ''){
                    $value = $SearchValues.$_
                    $Query = "$Query AND $_ LIKE `'$Value`'"
                }
                
            }
            Try{
                $Data = Get-SQLQuery $Query
                $dataGrid.Rows.Clear()
                $Data | ForEach-Object{
                    [datetime]$date = $_.LastReviewed
                    $lastReviewed = '{0:yyyy-MM-dd}' -f $Date
                    $dataGrid.Rows.Add($_.KBID,$_.ShortText,$_.Download,$_.AppliesTo,$lastReviewed,$_.Link,$_.Notes)
                }
            }
            Catch{}#Write-Error $_}
            $Progressbar.Hide()
        }

        $OnLoadForm_StateCorrection=
        {#Correct the initial state of the form to prevent the .Net maximized form issue
        	$form1.WindowState = $InitialFormWindowState
        }
        
        # Menu button clicks
        
        $toolEdit_Click = {
            If($EditMode -eq $False){
                $toolEdit.Checked = $True
                $EditMode = $True
                $MainMenu.Items.Add($editMenu) | out-null
                $datagrid.columns | ForEach-Object{$_.ReadOnly = $False}
            }
            Else{
                $toolEdit.Checked = $False
                $EditMode = $False
                $MainMenu.Items.Remove($editMenu) | out-null
                $datagrid.columns | ForEach-Object{$_.ReadOnly = $True}
            }
        }
        
        $fileExport_Click = {
            $File = SaveFileDialog -Filetype "csv"
            If($File -ne "Cancel"){
                $Progressbar.Show()
                "KBID,ShortText,Download,AppliesTo,LastReviewed,Link,Notes" | Out-File $File
                $dataGrid.Rows | ForEach-Object{
                    $Cells = $_.cells
                    $KBID = ($cells | Where-object{$_.OwningColumn.Name -eq "KBID"}).Value
                    $ShortText = ($cells | Where-object{$_.OwningColumn.Name -eq "ShortText"}).Value
                    $Download = ($cells | Where-object{$_.OwningColumn.Name -eq "Download"}).Value
                    $AppliesTo = ($cells | Where-object{$_.OwningColumn.Name -eq "AppliesTo"}).Value
                    $LastReviewed = ($cells | Where-object{$_.OwningColumn.Name -eq "LastReviewed"}).Value
                    $Link = ($cells | Where-object{$_.OwningColumn.Name -eq "Link"}).Value
                    $Notes = ($cells | Where-object{$_.OwningColumn.Name -eq "Notes"}).Value
                    If($KBID -ne $null){
                        "$KBID,$ShortText,$Download,$AppliesTo,$LastReviewed,$Link,$Notes" | Out-File $File -Append
                    }                    
                }
                $Progressbar.Hide()
            }
        }
        
        $toolClear_Click = {
            $dataGrid.Rows.Clear()
            
            $KBIDsearch.Text = ''
            $ShortTextSearch.Text = ''
            $DownloadSearch.Text = "True"
            $AppliestoSearch.Text = ''
            $ReviewSearch.Text = ''
            $LinkSearch.Text = ''
            $NotesSearch.Text = ''
        }
        
        $toolSave_Click = {
            .$saveloadOnLoad
            $saveForm.Controls.Add($listView)
            $saveForm.ShowDialog()| Out-Null
        }
        $toolLoad_Click = {
            .$saveloadOnLoad
            $loadForm.Controls.Add($listView)
            $loadForm.ShowDialog()| Out-Null
        }
        
        $fileQuit_Click = {
            $form1.Close()
        }
        
        $SaveButton_OnClick = {
            $new = $saves.CreateElement('Save')
            $Name = $SaveBox.Text
            $new.SetAttribute('Name',"$($SaveBox.Text)")
            $new.SetAttribute('Query',"$Query")
            Try{($saves.SavedSearches.Save | Where-Object{$_.Name -eq $Name}).RemoveAll()}Catch{}
            $saves.SavedSearches.AppendChild($new)
            $saves.Save($savepath)
            #clean up bad elements
            [xml]$Clean = Get-Content $savepath | Where-Object{($_ -NotMatch '') -or ($_ -NotMatch 'Save Name=""')}
            $Clean.Save($savepath)
            $saveForm.Controls.Remove($listView)
            $saveForm.Close()
        }
        $LoadButton_OnClick = {
            $Query = ($saves.SavedSearches.Save | Where-object {$_.Name -Match "$($loadBox.Text)"}).Query
            $loadForm.Controls.Remove($listView)
            $loadForm.Close()
            $Progressbar.Show()
            $KBIDsearch.Text = ''
            $ShortTextSearch.Text = ''
            $DownloadSearch.Text = "True"
            $AppliestoSearch.Text = ''
            $ReviewSearch.Text = ''
            $LinkSearch.Text = ''
            $NotesSearch.Text = ''
            Try{
                $Data = Get-SQLQuery $Query
                $dataGrid.Rows.Clear()
                $Data | ForEach-Object{
                    [datetime]$date = $_.LastReviewed
                    $lastReviewed = '{0:yyyy-MM-dd}' -f $Date
                    $dataGrid.Rows.Add($_.KBID,$_.ShortText,$_.Download,$_.AppliesTo,$lastReviewed,$_.Link,$_.Notes)
                }
            }
            Catch{Write-Error $_}
            $Progressbar.Hide()
        }
        
        $listView_OnSelected = {
            $SaveBox.Text = $listView.SelectedItems[0].Text
            $loadBox.Text = $listView.SelectedItems[0].Text
        }
        
        $saveloadOnLoad = {
            $loadBox.Text = ''
            $SaveBox.Text = ''
            $listView.Items.Clear()
            [xml]$Clean = Get-Content $savepath | Where-Object{($_ -NotMatch '') -or ($_ -NotMatch 'Save Name=""')}
            $Clean.Save($savepath)
            $saves = [xml](Get-Content $savepath)
            $saves.SavedSearches.Save | ForEach-Object{
                $listView.Items.Add("$($_.Name)")|Out-Null
            }
        }
        
        $editClear_Click = {
            $new = "`r`n`r`n"
            $new | out-file $savepath
        }
        
        $editUpdate_Click = {
        
            $dataGrid.Rows | ForEach-Object{
                $Cells = $_.cells
                $KBID = ($cells | Where-object{$_.OwningColumn.Name -eq "KBID"}).Value
                If($KBID -ne $null){
                    $ShortText = ($cells | Where-object{$_.OwningColumn.Name -eq "ShortText"}).Value
                    $Download = ($cells | Where-object{$_.OwningColumn.Name -eq "Download"}).Value
                    $AppliesTo = ($cells | Where-object{$_.OwningColumn.Name -eq "AppliesTo"}).Value
                    [Datetime]$LastReviewed = ($cells | Where-object{$_.OwningColumn.Name -eq "LastReviewed"}).Value
                    $Link = ($cells | Where-object{$_.OwningColumn.Name -eq "Link"}).Value
                    $Notes = ($cells | Where-object{$_.OwningColumn.Name -eq "Notes"}).Value
                
                    $Columns = "KBID","ShortText","Download","AppliesTo","LastReviewed","Link","Notes"
                    $Values = "`'$KBID`'","`'$ShortText`'","`'$Download`'","`'$AppliesTo`'","`'$LastReviewed`'","`'$Link`'","`'$Notes`'"
                    $UpdateCMD = "UPDATE hotfix_primary SET $($c = $Columns.count;(1..($c-1) | ForEach-Object{"$($Columns[$_]) = $($Values[$_])"}) -Join ',') WHERE KBID = `'$KBID`';"
                    Process-SQLCmd $UpdateCMD
                }                    
            }
            
        }

        #----------------------------------------------
        #region Generated Form Code
        $form1.ClientSize = "853,485"
        $form1.DataBindings.DefaultDataSourceUpdateMode = 0
        $form1.Text = "KB article browser"
        ### Main Menu Settings
        $MainMenu = new-object System.Windows.Forms.MenuStrip
        $MainMenu.Location = "0,0"
        $MainMenu.Name = "MainMenu"
        $MainMenu.Size = "1000,620"
        $MainMenu.TabIndex = 0
        $MainMenu.Text = "Main Menu"
        
        ## File Menu settings
        $fileMenu = new-object System.Windows.Forms.ToolStripMenuItem('&fileMenu')
        $fileMenu.Size = "35,20"
        $fileMenu.Text = "File"
        
        # Quit button
        $fileQuit = new-object System.Windows.Forms.ToolStripMenuItem('&Quit')
        $fileQuit.add_Click($fileQuit_Click)
        # Save button
        $fileExport = new-object System.Windows.Forms.ToolStripMenuItem('&Export list to CSV')
        $fileExport.Add_Click($fileExport_Click)
        
        # Add to file menu
        $fileMenu.DropDownItems.Add($fileExport) | out-null
        $fileMenu.DropDownItems.Add($fileQuit) | out-null
        
        ## Tool Menu settings
        $toolMenu = new-object System.Windows.Forms.ToolStripMenuItem('&toolMenu')
        $toolMenu.Size = "35,20"
        $toolMenu.Text = "Tools"
        
        $toolSave = new-object System.Windows.Forms.ToolStripMenuItem('&Save search')
        $toolSave.add_Click($toolSave_Click)
        
        # Copy button
        $toolLoad = new-object System.Windows.Forms.ToolStripMenuItem('&Load search')
        $toolLoad.add_Click($toolLoad_Click)
        
        # Copy button
        $toolEdit = new-object System.Windows.Forms.ToolStripMenuItem('&Edit mode')
        $toolEdit.add_Click($toolEdit_Click)

        # Clear button
        $toolClear = new-object System.Windows.Forms.ToolStripMenuItem('&Clear/Reset')
        $toolClear.add_Click($toolClear_Click)

        # Add to tool menu
        $toolMenu.DropDownItems.Add($toolSave) | out-null
        $toolMenu.DropDownItems.Add($toolLoad) | out-null
        $toolMenu.DropDownItems.Add($toolEdit) | out-null
        $toolMenu.DropDownItems.Add($toolClear) | out-null
        
        ## Edit Menu settings
        $editMenu = new-object System.Windows.Forms.ToolStripMenuItem('&editMenu')
        $editMenu.Size = "35,20"
        $editMenu.Text = "Edit"
        
        # Clear saved searches button
        $editClear = new-object System.Windows.Forms.ToolStripMenuItem('&Remove all saved searches')
        $editClear.add_Click($editClear_Click)
        
        # Update button
        $editUpdate = new-object System.Windows.Forms.ToolStripMenuItem('&Update database')
        $editUpdate.add_Click($editUpdate_Click)
        
        # Add to tool menu
        $editMenu.DropDownItems.Add($editUpdate) | out-null
        $editMenu.DropDownItems.Add($editClear) | out-null
        
        # Add main menu buttons
        $MainMenu.Items.Add($fileMenu) | out-null
        $MainMenu.Items.Add($toolMenu) | out-null
        
        $form1.Controls.Add($MainMenu) | out-null
        
        #######Searchboxes##########
        
        $NotesSearch.DataBindings.DefaultDataSourceUpdateMode = 0
        $NotesSearch.Location = "653,64"
        $NotesSearch.Name = "NotesSearch"
        $NotesSearch.Size = "93,20"
        $NotesSearch.TabIndex = 15

        $form1.Controls.Add($NotesSearch)

        $LinkSearch.DataBindings.DefaultDataSourceUpdateMode = 0
        $LinkSearch.Location = "553,64"
        $LinkSearch.Name = "LinkSearch"
        $LinkSearch.Size = "94,20"
        $LinkSearch.TabIndex = 14

        $form1.Controls.Add($LinkSearch)
        
        $DownloadSearch.DataBindings.DefaultDataSourceUpdateMode = 0
        $DownloadSearch.FormattingEnabled = $False
        $DownloadSearch.Items.Add("True")|Out-Null
        $DownloadSearch.Items.Add("False")|Out-Null
        $DownloadSearch.Location = "253,64"
        $DownloadSearch.Name = "DownloadSearch"
        $DownloadSearch.Size = "94,21"
        $DownloadSearch.TabIndex = 10
        $DownloadSearch.SelectedItem = "True"
        $DownloadSearch.DropDownStyle = 2
        
        $form1.Controls.Add($DownloadSearch)
        
        $ReviewSearch.DataBindings.DefaultDataSourceUpdateMode = 0
        $ReviewSearch.Location = "453,65"
        $ReviewSearch.Name = "ReviewSearch"
        $ReviewSearch.Size = "94,20"
        $ReviewSearch.TabIndex = 6

        $form1.Controls.Add($ReviewSearch)

        $AppliestoSearch.DataBindings.DefaultDataSourceUpdateMode = 0
        $AppliestoSearch.Location = "353,65"
        $AppliestoSearch.Name = "AppliestoSearch"
        $AppliestoSearch.Size = "94,20"
        $AppliestoSearch.TabIndex = 5

        $form1.Controls.Add($AppliestoSearch)

        $ShortTextSearch.DataBindings.DefaultDataSourceUpdateMode = 0
        $ShortTextSearch.Location = "153,65"
        $ShortTextSearch.MaxLength = 128
        $ShortTextSearch.Name = "ShortTextSearch"
        $ShortTextSearch.Size = "94,20"
        $ShortTextSearch.TabIndex = 3

        $form1.Controls.Add($ShortTextSearch)

        $KBIDsearch.DataBindings.DefaultDataSourceUpdateMode = 0
        $KBIDsearch.Location = "53,65"
        $KBIDsearch.MaxLength = 7
        $KBIDsearch.Name = "KBIDsearch"
        $KBIDsearch.Size = "94,20"
        $KBIDsearch.TabIndex = 2

        $form1.Controls.Add($KBIDsearch)
        
        $searchButton.Anchor = 9

        $searchButton.DataBindings.DefaultDataSourceUpdateMode = 0
        $searchButton.Location = "752,64"
        $searchButton.Name = "searchButton"
        $searchButton.Size = "88,21"
        $searchButton.TabIndex = 1
        $searchButton.Text = "Search"
        $searchButton.UseVisualStyleBackColor = $True
        $searchButton.add_Click($searchButton_Click)

        $form1.Controls.Add($searchButton)
        
        #####Labels and the rest#####

        $label7.DataBindings.DefaultDataSourceUpdateMode = 0

        $label7.Location = "653,43"
        $label7.Name = "label7"
        $label7.Size = "100,15"
        $label7.TabIndex = 17
        $label7.Text = "Notes"

        $form1.Controls.Add($label7)

        $label6.DataBindings.DefaultDataSourceUpdateMode = 0

        $label6.Location = "554,43"
        $label6.Name = "label6"
        $label6.Size = "100,14"
        $label6.TabIndex = 16
        $label6.Text = "Link"

        $form1.Controls.Add($label6)

        $label5.DataBindings.DefaultDataSourceUpdateMode = 0

        $label5.Location = "454,43"
        $label5.Name = "label5"
        $label5.Size = "93,18"
        $label5.TabIndex = 13
        $label5.Text = "Last Review Date"

        $form1.Controls.Add($label5)

        $label4.DataBindings.DefaultDataSourceUpdateMode = 0

        $label4.Location = "353,43"
        $label4.Name = "label4"
        $label4.Size = "94,15"
        $label4.TabIndex = 12
        $label4.Text = "AppliesTo"

        $form1.Controls.Add($label4)

        $Progressbar.DataBindings.DefaultDataSourceUpdateMode = 0
        $Progressbar.Location = "13,437"
        $Progressbar.Name = "Progressbar"
        $Progressbar.Size = "828,36"
        $Progressbar.Style = 2
        $Progressbar.TabIndex = 11
        $Progressbar.Anchor = 14

        $form1.Controls.Add($Progressbar)
        $Progressbar.Hide()


        $label3.DataBindings.DefaultDataSourceUpdateMode = 0

        $label3.Location = "253,43"
        $label3.Name = "label3"
        $label3.Size = "94,18"
        $label3.TabIndex = 9
        $label3.Text = "Hotfix available"

        $form1.Controls.Add($label3)

        $label2.DataBindings.DefaultDataSourceUpdateMode = 0

        $label2.Location = "153,44"
        $label2.Name = "label2"
        $label2.Size = "94,18"
        $label2.TabIndex = 8
        $label2.Text = "Short Text"

        $form1.Controls.Add($label2)

        $label1.DataBindings.DefaultDataSourceUpdateMode = 0
        $label1.Location = "53,44"
        $label1.Name = "label1"
        $label1.RightToLeft = 0
        $label1.Size = "94,18"
        $label1.TabIndex = 7
        $label1.Text = "KB ID"
        $label1.add_Click($handler_label1_Click)

        $form1.Controls.Add($label1)
        
        ####DATA GRID COLUMNS######
        
        $dataGrid.Anchor = 15
        $KBIDColumn = New-Object System.Windows.Forms.DataGridViewTextBoxColumn
        $KBIDColumn.HeaderText = "KB ID"
        $KBIDColumn.Name = "KBID"
        $KBIDColumn.ReadOnly = $True
        $KBIDColumn.Width = 100

        $dataGrid.Columns.Add($KBIDColumn)|Out-Null
        
        $ShortTextColumn = New-Object System.Windows.Forms.DataGridViewTextBoxColumn
        $ShortTextColumn.HeaderText = "Short Text"
        $ShortTextColumn.Name = "ShortText"
        $ShortTextColumn.ReadOnly = $True
        $ShortTextColumn.Width = 100

        $dataGrid.Columns.Add($ShortTextColumn)|Out-Null
        
        $DownloadColumn = New-Object System.Windows.Forms.DataGridViewTextBoxColumn
        $DownloadColumn.HeaderText = "Hotfix available"
        $DownloadColumn.Name = "Download"
        $DownloadColumn.ReadOnly = $True
        $DownloadColumn.Width = 100

        $dataGrid.Columns.Add($DownloadColumn)|Out-Null
        
        $AppliesToColumn = New-Object System.Windows.Forms.DataGridViewTextBoxColumn
        $AppliesToColumn.HeaderText = "Applies to"
        $AppliesToColumn.Name = "AppliesTo"
        $AppliesToColumn.ReadOnly = $True
        $AppliesToColumn.Width = 100

        $dataGrid.Columns.Add($AppliesToColumn)|Out-Null
        
        $DateColumn = New-Object System.Windows.Forms.DataGridViewTextBoxColumn
        $DateColumn.HeaderText = "Last Review date"
        $DateColumn.Name = "LastReviewed"
        $DateColumn.ReadOnly = $True
        $DateColumn.Width = 100

        $dataGrid.Columns.Add($DateColumn)|Out-Null
        
        $LinkColumn = New-Object System.Windows.Forms.DataGridViewTextBoxColumn
        $LinkColumn.HeaderText = "Link"
        $LinkColumn.Name = "Link"
        $LinkColumn.ReadOnly = $True
        $LinkColumn.Width = 100

        $dataGrid.Columns.Add($LinkColumn)|Out-Null
        
        $NotesColumn = New-Object System.Windows.Forms.DataGridViewTextBoxColumn
        $NotesColumn.HeaderText = "Notes"
        $NotesColumn.Name = "Notes"
        $NotesColumn.ReadOnly = $False
        $NotesColumn.Width = 100

        $dataGrid.Columns.Add($NotesColumn)|Out-Null
        
        $dataGrid.DataBindings.DefaultDataSourceUpdateMode = 0
        $dataGrid.Location = "12,91"
        $dataGrid.Name = "dataGrid"
        $dataGrid.Size = "828,382"
        $dataGrid.TabIndex = 0
        $dataGrid.add_CellContentClick($handler_dataGrid_CellContentClick)

        $form1.Controls.Add($dataGrid)

        #endregion Generated Form Code

        #Save the initial state of the form
        $InitialFormWindowState = $form1.WindowState
        #Init the OnLoad event to correct the initial state of the form
        $form1.add_Load($OnLoadForm_StateCorrection)
        
        ### SAVE SEARCH FORM SETTINGS
        $saveForm.ClientSize = "246,196"
        $saveForm.DataBindings.DefaultDataSourceUpdateMode = 0
        $saveForm.Name = "saveForm"
        $saveForm.Text = "Save current search"
        $form1.add_Load($saveloadOnLoad)

        $saveBox.Anchor = 14
        $saveBox.DataBindings.DefaultDataSourceUpdateMode = 0
        $saveBox.Location = "1,133"
        $saveBox.Name = "saveBox"
        $saveBox.Size = "245,20"
        $saveBox.TabIndex = 2

        $saveForm.Controls.Add($saveBox)

        $SaveButton.Anchor = 14

        $SaveButton.DataBindings.DefaultDataSourceUpdateMode = 0

        $SaveButton.Location = "1,159"
        $SaveButton.Name = "SaveButton"
        $SaveButton.Size = "245,38"
        $SaveButton.TabIndex = 1
        $SaveButton.Text = "Save"
        $SaveButton.UseVisualStyleBackColor = $True
        $SaveButton.add_Click($SaveButton_OnClick)

        $saveForm.Controls.Add($SaveButton)

        $listView.Anchor = 15
        $listView.DataBindings.DefaultDataSourceUpdateMode = 0
        $listView.Location = "1,1"
        $listView.Name = "listView"
        $listView.Size = "245,121"
        $listView.TabIndex = 0
        $listView.add_MouseClick($listView_OnSelected)
        #$listView.add_KeyPress($listView_OnDelete)
        $listView.View = 3
        
        ### LOAD SEARCH FORM SETTINGS
        $loadForm.ClientSize = "246,196"
        $loadForm.DataBindings.DefaultDataSourceUpdateMode = 0
        $loadForm.Name = "loadForm"
        $loadForm.Text = "Load current search"
        $form1.add_Load($saveloadOnLoad)

        $loadBox.Anchor = 14
        $loadBox.DataBindings.DefaultDataSourceUpdateMode = 0
        $loadBox.Location = "1,133"
        $loadBox.Name = "loadBox"
        $loadBox.Size = "245,20"
        $loadBox.TabIndex = 2

        $loadForm.Controls.Add($loadBox)

        $loadButton.Anchor = 14

        $loadButton.DataBindings.DefaultDataSourceUpdateMode = 0

        $loadButton.Location = "1,159"
        $loadButton.Name = "loadButton"
        $loadButton.Size = "245,38"
        $loadButton.TabIndex = 1
        $loadButton.Text = "load"
        $loadButton.UseVisualStyleBackColor = $True
        $loadButton.add_Click($loadButton_OnClick)
        
        $loadForm.Controls.Add($listView)
        $loadForm.Controls.Add($loadButton)
        
        #Show the Form
        If(!(Test-Path $savePath)){.$editClear_Click}
        $form1.ShowDialog()| Out-Null
    }
    
    Function Process-SQLCmd {
    Param(
        [Parameter(Mandatory=$true)]
        [String]$Command,
        [Parameter(Mandatory=$false)]
        [String]$ConnectionString = 'Server=LocalHost\SQL;Database=ClientMaintenance;Trusted_Connection=True;'
    )
        $connection = New-Object System.Data.SqlClient.SqlConnection
        $connection.ConnectionString = $ConnectionString
        $connection.Open()
        $cmd = New-Object System.Data.SQLClient.SQLCommand
        $cmd.Connection = $connection
        $cmd.CommandText = $Command
        Try{$cmd.ExecuteNonQuery()}
        Catch{
            If(!($_.Exception -match 'Violation of PRIMARY KEY constraint')){
                $_.Exception | Out-file "$Mypath\error.log" -append
                $command | Out-file "$Mypath\error.log" -append
            }
        }
        $connection.Close()
    }
    
    Function Get-SQLQuery{
    Param(
        [Parameter(Mandatory=$true)]
        [String]$SQLQuery,
        [Parameter(Mandatory=$false)]
        [String]$ConnectionString = 'Server=LocalHost\SQL;Database=ClientMaintenance;Trusted_Connection=True;'
    )
        $connection = New-Object System.Data.SqlClient.SqlConnection
        $connection.ConnectionString = "$ConnectionString"
        $connection.Open()
        $Command = New-Object System.Data.SQLClient.SQLCommand
        $Command.CommandTimeout = 300
        $Command.Connection = $Connection
        $Command.CommandText = $SQLQuery
        Try{
            $Reader = $Command.ExecuteReader()
            $Counter = $Reader.FieldCount
            
            $DataTable = New-Object system.Data.DataTable
            for($i = 0; $i -lt $counter; $i++){
                $Column = New-Object system.Data.DataColumn $Reader.GetName($i),([string])
                $DataTable.Columns.Add($Column)
            }
            
            while ($Reader.Read()) {
                $strData = @()
                for ($i = 0; $i -lt $Counter; $i++) {
                    $strData += [String]$Reader.GetValue($i)
                }
                $DataTable.Rows.Add($strData) | out-null
            }
            $Reader.Close()
            $Data = $Datatable
        }
        Catch{
            Write-Error $_
            $Data  = $_.Exception
        }
        Finally{
            $Connection.Close()
        }
        Return $Data
    }
}
Process{
    
    Call-Form
}
End{}

Index all microsoft hotfixes

Microsoft has a crappy hotfix index mechanism. I want to change this, and I’ve done this by indexing all support.microsoft articles myself.

To do this you need a SQL server with the following table configuration
Capture

Then you run the indexer script. It goes through all hotfixes inefficiently but gets the job done.
All support.microsoft.com/kb pages are indexed. From 1 to 4000000

Result in MSSQL looks something like this:

Capture

Due to web formatting the script can’t be displayed here. Instead I’ve linked it below as a .txt file
Script

Of course, there is a hotfix browser that is used to search and view these but it’s not done yet.

Project: 3D Printer from scratch. Part 3 – Electronics, issues and blowing fuses

The last parts were delivered from ebay on the 3rd of march. This took a bit longer than usual as I managed to order during the chinese new year.

With all the parts at hand i decided to start building, while the paint on the frame dries I started working on the electronics.

I converted a old Corsair cx430 to work with the printer. At 12v and 430W the supply can output about 35 amps which is more than enough.
Here is a picture of the Mega 2560+RAMPS 1.4 with 5 stepper drivers mounted, 5 motors connected and the hotend connected.
IMG_0701

The firmware I’m using is Marlin. Marlin is a deviation on Sprinter and has LCD support amongst other improvements.

Image of the motors, both Z axises are running.
From left to right:
X axis, Y axis, 2x Z axis, Extruder motor.
IMG_0702

When connecting the LCD I noticed something was off, the display shows garbage and nothing can be read from it.
IMG_0709
The almighty google suggest multiple possible errors.
First thing to troubleshoot is to see if there is any noticable soldering junk creating crosstalk, I checked both the LCD PCB and Mega2560+RAMPS PCB but I could not find any. I then proceeded to checking the Marlin code to see if I had made any configuration errors.
Unfortunately I had no luck there either.
As a LCD is not required I decided to move on. When the printer is up and running I will probably deal with the LCD errors.

Then I went ahead and configured the endstops. This proved an issue. I’m using 4 pin Mech Endstop v1.2. I had a hard time figuring out which pin did what and after more google I found out more people had made the mistake to wire them wrong and burn the arduino board.
My approach was to connect a LED to the endstop and a tiny bit of voltage from the PSU.
Unfortunately my calculations were off and I managed to burn the fuse on the PSU! 🙁
Here is an image of the PSU, next to the big brown cable is the burned fuse.
IMG_0707
Soldering off the power cords and fuse and from the PSU was easy, however with the PSU built to be compact it was not easy to solder a new 3A fuse.
Here we can see the PSU out of the case with the old fuse on the side.
IMG_0713

With the endstop issues solved and PSU works great the main job left is to assemble the printer.
More updates to come!

Ps. More images
IMG_0710 IMG_0703 IMG_0700 pronterface

Project: 3D printer from scratch. Part 2 – Printed parts delivery

Update!

I got a box yesterday with some mysterious contents.
20140204_075644

Opening it revealed the table of contents, delivery took exactly one week.
20140204_075730

I’m very pleased, this is the exact ebay item i bought

Since images can tell the story better than I can this post will be mostly  pictures.

All included parts scattered on the floor, cat was not included in the box.20140204_075933
For those wondering, these parts were ofcourse themselves printed. Gears are made using PLA filament and the rest are printed with ABS filament.
20140204_075928

 

Closeup of the gear teeth
IMG_2740 IMG_2738
Usb flash drive for scale
IMG_2749 IMG_2746   IMG_2745

Project: 3D printer from scratch. Part 1 – The Shoppingcart.

I’ve always wanted a 3D printer, but the pricetag has been so high that I’ve gotten discouraged in the past.

However with the increasing community support of open sourced 3D printers the accessibility and price has dropped significantly if you’re willing to do some manual work.
As this is not an easy task to build a 3D printer, and that it will most likely mostly stand idle I thought it would be good to have a partner in crime. Knowing my brother also wants a 3D printer I approached him and we decided to build one together.

After a period of research I decided to build the RepRap Prusa i3.

The RepRap project (Link) is an open source 3D printer project founded by Dr. Adrian Bowyer. The build area is about 20x20x20 cm or 8x8x8 inches. This enables about 8000cm3 or 8 litre build.

The filament is about 40 USD/1kg which allows for a ton of prints.

The finished product should look something like this, but with an LCD display and SD card slot to enable standalone printing.

Prusa i3

Before we can begin building we need the parts. With budget in mind I’ve decided to shop the bulk of hardware from Ebay.

Here is the shopping list:

 

  • 1x Prusa i3 Printed parts (boxed variant)
  • 1x Greg’s JHead Extruder (cold end)
  • 1x M8 hobbed bolt (to pull the filament)
  • 1x J-Head Pre assembled Hotend – 0.3mm nozzle – 1.75mm filament (includes thermistor and resistor)
  • 1x HotEnd Aluminum mount plate for J-Head
  • 1x HeatBed mk2
  • 1x Aluminum Bed mount plate
  • 1x 10k ohm thermistor
  • 1x red 3mm LED
  • 1x Borosilicate glass
  • 5x Nema 17 stepper motor (1.8° stepping angle)
  • 10x LM8UU linear ball bearing
  • 2x 5x5mm coupling (D20L25)
  • 2x GT2 5mm pulley
  • 2m GT2 timing belt
  • 1x LCD 2004 controller
  • 1x Arduino Mega 2560
  • 1x RAMPS 1.4 shield
  • 5x A4988 stepper driver + heatsinks
  • 3x Mechanical endstop switches
  • 1x 624 ball bearing
  • 4x 608 ball bearing

 

I also bought various cables, connectors and metal clamps, 40mm fans, but these do not need to be defined.

Cost: 420 USD included shipping. This comes out at around 2700 sek.

Next we need to buy material for the frame which I will aquire from the local hardware store.

We need:

 

  • Wood/chipboard to build the frame.
  • Centerless smooth metal rods
  • Threaded metal rods
  • A ton of nuts
  • Power supply (Any old ATX PSU should suffice)
  • Black paint for the frame

 

For build instructions there is a great unity 3d model build instruction to be found.

http://www.carlos-sanchez.com/Prusa3/

To be continued…

Solve sudoku – WIP!

[CmdletBinding()]
Param()
Process{
    # Initial process. Clear all grids with hints.
    
    $Hash1 = 1 # Set Datahash 1 and 2 to different values.
    $Hash2 = 0
    While($Hash1 -notmatch $Hash2){
        $Hash1 = (1..9 | ForEach-Object{($Data | Where-Object{$_."ID"}).$_}).count
        Process-Sudoku
        $Hash2 = (1..9 | ForEach-Object{($Data | Where-Object{$_."ID"}).$_}).count
    }
}
Begin{
    #
    $MyPath = Split-Path $MyInvocation.MyCommand.path -Parent

    $Data = Import-Csv -Path "$MyPath\sudoku.txt" -Delimiter " "

    For($i = 1;$i -le 9;$i++){
        $currentRow = ($Data | Where-Object{$_."ID" -eq $i})
        For($n = 1;$n -le 9;$n++){
            $cell = [int32]($currentRow.$n)
            If($cell -eq 0){
                $currentRow.$n = (1..9)
            }
            Else{
                $currentRow.$n = [array]$cell
            }
        }
    }
    Function Process-Sudoku{
        
        # Process Horizontal
        For($i = 1;$i -le 9;$i++){
            If($Hash1 -le 162){} ## DEEBUUUGG
            $currentRow = $data[($i-1)]
            $exists = 1..9 | Foreach-Object{If(($currentRow.$_).count -eq 1){$currentRow.$_}}
            $NotSolved = 1..9 | Where-Object{($currentRow.$_).count -gt 1}
            $NotSolved | ForEach-Object{
                [Array]$NewArray = $currentRow.$_ | ForEach-Object{if($exists -match $_){}Else{$_}}
                $currentRow.$_ = $NewArray
            }
        }

        # Process Verical
        For($i = 1;$i -le 9;$i++){
            $CurrentLine = @{}
            For($n = 1;$n -le 9;$n++){
                $currentRow = $Data[($n-1)]
                $CurrentLine += @{$n = $currentRow.$i}
            }
            $Exists = 1..9 | Foreach-Object{If(($CurrentLine.$_).count -eq 1){$currentLine.$_}}
            $NotSolved = 1..9 | Where-Object{($CurrentLine.$_).count -gt 1}
            $NotSolved | ForEach-Object{
                For($n = 1;$n -le 9;$n++){
                    $currentRow = $Data[($_-1)]
                    [Array]$NewArray = $currentRow.$i | ForEach-Object{if($exists -match $_){}Else{$_}}
                    $currentRow.$i = $NewArray
                }
            }
        }
        # Process grid
        For($i = 1;$i -le 9;$i++){
            
            If($i -le 3){$Mod = 0}
            ElseIf($i -gt 6){$Mod = 6}
            Else{$Mod = 3}

            $CurrentGrid = @{}
            $c = 1
            For($n = 1;$n -le 3;$n++){
                $currentRow = $Data[($n+$mod-1)]
                For($x = 1;$x -le 3;$x++){
                    
                    $CurrentGrid += @{$c = $currentRow.$x}
                    $c++
                }
            }
            $exists = 1..9 | Foreach-Object{If(($CurrentGrid.$_).count -eq 1){$CurrentGrid.$_}}
            $NotSolved = 1..9 | Where-Object{($CurrentGrid.$_).count -gt 1}
            $NotSolved | ForEach-Object{
                If($_ -le 3){$row = 1}
                ElseIf($_ -ge 7){$row = 3}
                Else{$row = 2}
                $currentRow = $Data[($row+$mod-1)]
                [Array]$NewArray = $currentRow.$_ | ForEach-Object{if($exists -match $_){}Else{$_}}
                $currentRow.$_ = $NewArray
            }
        }
    } # End process function
}
End{
    $Data | Format-Table -AutoSize
}

Input

ID 1 2 3 4 5 6 7 8 9
1 9 0 0 0 0 0 1 2 0
2 0 0 2 3 0 0 0 0 0
3 4 0 0 0 0 5 9 6 0
4 0 8 0 2 0 0 6 0 0
5 0 0 0 0 5 0 0 0 0
6 0 0 1 0 0 9 0 3 0
7 0 7 6 9 0 0 0 0 1
8 0 0 0 0 0 1 7 0 0
9 0 9 8 0 0 0 0 0 4

Find primes, the proper way

I noticed that the earlier way to find primes is really fast for small numbers, but it is really really slow for number >5000

To shorten this, and provide a proper track of what primes are found as well as looping endlessly I’ve created this thing below.

Place it as prime.ps1 in a folder with read/write access.

The code below writes lines in a file called primes.log and keeps a progress of the last processed number (stored in progress.log).
This allows the script to resume the last location where it was terminated.
It also uses a better algorithm to determine if the given number is a prime or not, resulting in a steady 30 seconds to calculate 0-5000

Now the algorithm is not perfect (could check common divisible numbers first and some pattern matching) but it produces a steady stream of numbers in a fairly quick fashion.

[CmdletBinding()]
Param()
Process{
    
    If(!([int]$currentNumber = Get-Content $progressfile -ErrorAction Ignore)){[int]$currentNumber = $lastPrime+1}
    Write-Verbose "Starting at $currentNumber"

    "$(Get-Date -UFormat "%Y-%m-%d %r")| Starting at $currentNumber" | Out-File $logfile -Append

    While($Loop){
        
        Write-Verbose "Processing $currentNumber"
        $prime = Test-Prime $currentNumber
        
        If($prime){
            $currentNumber | Out-File $file -Append
            "$(Get-Date -UFormat "%Y-%m-%d %r")| Found prime, $currentNumber" | Out-File $logfile -Append
            Write-host "Success, prime found! $currentNumber" -ForegroundColor Green
        }
        
        $currentNumber++
        $currentNumber | Out-File $progressfile
    }
}
Begin{
    
    $MyPath = Split-Path $MyInvocation.MyCommand.path -Parent
    
    $Loop = $true
    $logfile = "$MyPath\log.log"
    $file = "$MyPath\primes.log"
    $progressfile = "$MyPath\progress.log"
    Try{
        $primes = Get-Content $file -ErrorAction Ignore
        [int]$lastPrime = $primes[-1]
        Write-Verbose "File found, last prime is $lastprime"
    }
    Catch{
        Write-Verbose "File not found. Creating file and starting from 3"
        $lastPrime = 3
        New-Item -Path $file -ItemType file -Force
        "2","3" | ForEach-Object{$_ | Out-File $file -Append}
    }

    Function Test-Prime{
        Param($number)
        If($number -match "0$|2$|4$|5$|6$|8$"){
            return $false
        }
        [int]$DivNumber = $number/2
        #$numbers = 2..$DivNumber
        $i = 2
        While($i -le $DivNumber){
            If($number%$i -eq 0){
                return $false
            }
            $i++
        }
        return $true
    }
}