Powershell opc xml write UInt32 array - powershell

I've managed to read an OPC XML item with a Powershell script
$wsdl=(Join-path $PSScriptRoot "OpcXmlDa1.00.wsdl")
$global:Proxy = New-WebserviceProxy $wsdl –Namespace X
$global:Options = New-Object X.RequestOptions
$Options.ReturnItemName = $true;
$Options.ReturnItemTime = $true;
$global:ItemList = New-Object X.ReadRequestItemList
$global:RItemList = New-Object X.ReplyItemList
$global:Errors = New-Object X.OPCError
$global:Item = New-Object X.ReadRequestItem
# Read from OPC server
$Proxy.Url = "http://xxx.xx.xx.xxx:xxxx"
$Item.ItemName = "xxx/xxx/xxx"
$ItemList.Items = $Item
$Proxy.Read($Options, $ItemList, [ref]$RItemList, [ref]$Errors)
#Show result
$rTimeStamp = $RItemList.Items[0].Timestamp.ToString('yyyy-MM-dd HH:mm:ss')
$rItemName = $RItemList.Items[0].ItemName
$rValue = $RItemList.Items[0].Value
Write-Host ("{0}: {1}={2}" -f $rTimeStamp, $rItemName, $rValue)
In my result above I see the following in $RItemList.Items[0]
DiagnosticInfo:$null
Value: [UInt32[32]]
Quality: [OPCQuality]
ValueTypeQualifier: $null
ItemPath: ""
ItemName: "xxx/xxx/xxx"
ClientItemHandle: $null
Timestamp: 2020-12 19 01:00:00
TimestampSpecified: $true
ResultID: $null
and when I dive deeper into Value above I see that it is read as expected. So far so good...
Now to my problem; I cannot understand how to write to this same Item via another script.
This is my attempt
$wsdl=(Join-path $PSScriptRoot "OpcXmlDa1.00.wsdl")
$Global:Proxy = New-WebserviceProxy $wsdl –Namespace X
$Global:Options = New-Object X.RequestOptions
$Options.ReturnItemName = $true;
$Options.ReturnItemTime = $true;
$Options.ReturnDiagnosticInfo = $true;
$Global:writeItemList = New-Object X.WriteRequestItemList
$Global:RItemList = New-Object X.ReplyItemList
$Global:Errors = New-Object X.OPCError
$Global:ItemSessionRequest = New-Object X.ItemValue
$ItemSessionRequest.ValueTypeQualifier = 'xsd:unsignedInt' #<- I believe that this might be the issue?
$ItemSessionRequest.ItemPath = ""
$ItemSessionRequest.ItemName = "xxx/xxx/xxx"
$ItemSessionRequest.Value = #($x;$y;$z) #<- I have what I want to write in $x, $y, $z
# Write to OPC server
$Proxy.Url = "http://xxx.xx.xx.xxx:xxxx"
$ReturnValuesOnReply = $true
$writeItemList.Items = $ItemSessionRequest
$Proxy.Write($Options, $writeItemList, $ReturnValuesOnReply, [ref]$RItemList, [ref]$Errors)
I get two errors (I'm running via VSC-code).
Error #1:
DiagnosticInfo : This value to write cannot be NULL
Value :
Quality :
ValueTypeQualifier :
ItemPath :
ItemName : xxx/xxx/SessionRequest
ClientItemHandle :
Timestamp : 1970-01-01 00:59:59
TimestampSpecified : True
ResultID : http://opcfoundation.org/webservices/XMLDA/1.0/:E_BADTYPE
I've assumed that this has to do with my choice of $ItemSessionRequest.ValueTypeQualifier (where I've tried a lot of alternatives).
Error #2: This error comes if I run the script a second time in the same instance
Cannot convert argument "Options", with value: "X.RequestOptions", for "Write" to type "X.RequestOptions": "Cannot convert the "X.RequestOptions" value of type "X.RequestOptions" to type "X.Request
Options"."
At C:\bla.bla.ps1:79 char:1
+ $Proxy.Write($Options, $writeItemList, $ReturnValuesOnReply, [ref]$RI ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument
I guess this has something to do with the $Proxy variable
I'm grateful for help and I apologize if I missed something relevant, but I have done my best...

In this case problem no 1 was solved by the following
$ItemSessionRequest.Value = #($x;$y;$z)
A work around for problem no 2 was by using a new session inside the script, see 1

Related

Pivot Table directly from SQL using powershell

I am using a combination of powershell scripts I've found online (shown below) to create an Excel document with pivot tables. But, I've only found examples where the datasource for the pivot tables was data already-imported into the excel document via CSV, or other similar methods. I can get those to work. But, my problem is that I would like to bypass the step of loading .csv data into excel and then creating the pivot tables...my whole issue is my dataset is way too large for excel...so I want to create the pivot table directly from a SQL connection. I cannot figure out the right syntax to to set the datasource. Below is my closest attempt...should use "xlExternal" for the datasource? But how do I set the datasource then? Micorsoft's documentation/APIs are somwhat helpful, but their examples are all written in VB and they also use other methods I'm not even familiar with in VB, so I'm having a hard time translating the QueryTables.Add method and some others.
I believe the only 1 or 2 lines that I'm stuck on are this one:
$qt = $ws.QueryTables.Add("ODBC;DSN=$connectionString", $ws.Range("A1"),
$SQL)
And/or this one:
$PivotTable =
$wb.PivotCaches().Create($xlExternal,$selection,$xlPivotTableVersion10)
But my code gives the exception on this line:
$PivotTable.CreatePivotTable("R1C1","Tables1") | Out-Null
and the error is saying:
Exception from HRESULT: 0x800A03EC
At C:\Users\me\Desktop\NEWpsCode.ps1:107 char:1
+ $PivotTable.CreatePivotTable("R1C1","Tables1") | Out-Null
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], COMException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.COMException
So, here is my code:
$DirectoryToSaveTo='C:\Users\me\Desktop\'
$Filename='myfile'
$ServerName = "BLAH1"
$DatabaseName = "BLAH2"
$userName = "BLAH3"
$password = "BLAH4"
$connectionString = "Server = $ServerName; Database = $DatabaseName; User ID
= $userName; Password = $password;"
$sqlConnection = New-Object System.Data.SqlClient.SqlConnection
$connectionString
$sqlConnection.Open()
$xlCenter=-4108
$xlTop=-4160
$xlOpenXMLWorkbook=[int]51
$SQL=#"
SELECT Account1,Account2,Account3 from myTable
"#
$excel = New-Object -Com Excel.Application #open a new instance of Excel
$excel.Visible = $True
$wb = $Excel.Workbooks.Add()
$currentWorksheet=1
if ($currentWorksheet-lt 4)
{
$ws = $wb.Worksheets.Item($currentWorksheet)
}
else
{
$ws = $wb.Worksheets.Add()
}
$currentWorksheet += 1
$qt = $ws.QueryTables.Add("ODBC;DSN=$connectionString", $ws.Range("A1"),
$SQL)
$xlPivotTableVersion12 = 3
$xlPivotTableVersion10 = 1
$xlCount = -4112
$xlDescending = 2
$xlDatabase = 1
$xlExternal = 2
$xlHidden = 0
$xlRowField = 1
$xlColumnField = 2
$xlPageField = 3
$xlDataField = 4
$xlDirection = [Microsoft.Office.Interop.Excel.XLDirection]
$range1=$ws.range("A1")
$range1=$ws.range($range1,$range1.End($xlDirection::xlDown))
$range2=$ws.range("B1")
$range2=$ws.range($range2,$range2.End($xlDirection::xlDown))
$selection = $ws.range($range1, $range2)
$PivotTable =
$wb.PivotCaches().Create($xlExternal,$selection,$xlPivotTableVersion10)
$PivotTable.CreatePivotTable("R1C1","Tables1") | Out-Null
$filename = "$DirectoryToSaveTo$filename.xlsx"
if (test-path $filename ) { rm $filename }
$wb.SaveAs($filename, $xlOpenXMLWorkbook)
$wb.Saved = $True
$wb.Close()
$Excel.Quit()
$wb = $Null
$ws = $Null
$Excel=$Null
So, if anyone can please direct me to setting up a pivot table using SQL connection directly, that would be extremely helpful! Even a very simple example will help immensely.

Chart.point labels wont work with 2 charts created instead of 1

Working on charts in powershell display results from a windows ping to various servers.
This code works great on one chart.
It display's a stacked bar chart with failed pings in a red bar with number of failed pings displayed and if the Response Time is greater than some number it makes the bar yellow and labels it with the response time.
#region setup chart
$Chart = New-Object -TypeName System.Windows.Forms.DataVisualization.Charting.Chart
$Chart.Size = '600,750'
#$Chart.BackColor = [system.drawing.color]::Transparent
$ChartArea = New-Object -TypeName System.Windows.Forms.DataVisualization.Charting.ChartArea
$ChartArea.AxisX.Title = 'Response times'
$ChartArea.AxisY.Title = 'Time in MS '
$ChartArea.AxisX.Interval = '1'
$ChartArea.AxisX.LabelStyle.Enabled = $true
$ChartArea.AxisX.LabelStyle.Angle = 90
$Chart.ChartAreas.Add($ChartArea)
$Chart.Series.Add('Good')
$Chart.Series['Good']["DrawingStyle"] = "Cylinder"
$Chart.Series['Good'].ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::StackedBar
$Chart.Series['Good'].Color = [system.drawing.color]::Green
$Title = New-Object -TypeName System.Windows.Forms.DataVisualization.Charting.Title
$Chart.Titles.Add($Title)
$Chart.Titles[0].Text = 'Ping Monitor (Active Pings)'
#endregion chart
function processPINGS {
BEGIN {
$curpoint=0
$global:Chart.Series['Good'].points.clear()
}#begin
Process {
ForEach($key in $global:status.keys)
{
switch ($global:status[$key].FailedType){
0 {
$Value = $global:Chart.Series['Good'].Points.AddXY("$($global:status[$key].name)","$($global:status[$key].'Response Time')")
} #end case 0
1 { $Value = $global:Chart.Series['Good'].Points.AddXY("$($global:status[$key].name)","150")
$global:Chart.Series['Good'].points[$curpoint].color = [system.drawing.color]::RED
$global:Chart.Series['Good'].points[$curpoint].label = "$($global:status[$key].'Failed Pings') Failed Pings"
}#end case 1
2 {$Value=$global:Chart.Series['Good'].Points.AddXY("$($global:status[$key].name)","$($status[$key].'Response Time')")
$global:Chart.Series['Good'].points[$curpoint].color = [system.drawing.color]::Yellow
$global:Chart.Series['Good'].points[$curpoint].label = "$($global:status[$key].'Response Time') MS"
}#end case 2
}#end switch
$curpoint++
}#end foreach Key
}#end of process block
}#end of function processPINGS
$PingCount = 0
Do {
Ping-Machines
processPINGS
$Chart.SaveImage("$scriptpath\Chartgoodpings.png", "PNG")
Start-Sleep -seconds 30
}Until (PingCount -gt 120)
So now I wanted to make 2 charts one for machines that ping and one for machines that do not ping so someone can see quickly all the machines that are not pinging. I copied the chart setup as follows
$NoPingChart = New-Object -TypeName System.Windows.Forms.DataVisualization.Charting.Chart
$NoPingChart.Size = '600,750'
$NoPingChartArea = New-Object -TypeName System.Windows.Forms.DataVisualization.Charting.ChartArea
$NoPingChartArea.AxisX.Title = 'Response times'
$NoPingChartArea.AxisY.Title = 'Time in MS '
$NoPingChartArea.AxisX.Interval = '1'
$NoPingChartArea.AxisX.LabelStyle.Enabled = $true
$NoPingChartArea.AxisX.LabelStyle.Angle = 90
$NoPingChart.ChartAreas.Add($NoPingChartArea)
$NoPingChart.Series.Add('Good')
$NoPingChart.Series['Good']["DrawingStyle"] = "Cylinder"
$NoPingChart.Series['Good'].ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::StackedBar
$NoPingChart.Series['Good'].Color = [system.drawing.color]::RED
$Title = New-Object -TypeName System.Windows.Forms.DataVisualization.Charting.Title
$NoPingChart.Titles.Add($Title)
$NoPingChart.Titles[0].Text = 'Ping Monitor (Failed Pings)'
I then changed the processPings function to save the results for bad pings to the new chart as follows
function processPINGS {
BEGIN {
$curpoint=0
$global:Chart.Series['Good'].points.clear()
}#begin
Process {
ForEach($key in $global:status.keys)
{
switch ($global:status[$key].FailedType){
0 {
$Value = $global:Chart.Series['Good'].Points.AddXY("$($global:status[$key].name)","$($global:status[$key].'Response Time')")
} #end case 0
1 { $Value = $global:NoPingChart.Series['Good'].Points.AddXY("$($global:status[$key].name)","150")
$global:NoPingChart.Series['Good'].points[$curpoint].color = [system.drawing.color]::RED
$global:NoPingChart.Series['Good'].points[$curpoint].label = "$($global:status[$key].'Failed Pings') Failed Pings"
}#end case 1
2 {$Value=$global:Chart.Series['Good'].Points.AddXY("$($global:status[$key].name)","$($status[$key].'Response Time')")
$global:Chart.Series['Good'].points[$curpoint].color = [system.drawing.color]::Yellow
$global:Chart.Series['Good'].points[$curpoint].label = "$($global:status[$key].'Response Time') MS"
}#end case 2
}#end switch
$curpoint++
}#end foreach Key
}#end of process block
}#end of function processPINGS
I then ran the program again with the changes and as soon as it tried to create the points in case 1 or 2 I get the following errors.
The property 'Label' cannot be found on this object. Verify that the property exists and can be set.
At C:\ToolBox\PINGMonitor\pingChart-v3.ps1:94 char:87
+ ... nts[$curpoint].Label = "$($global:status[$key].'Failed Pings') Failed ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : PropertyNotFound
The property 'Label' cannot be found on this object. Verify that the property exists and can be set.
At C:\ToolBox\PINGMonitor\pingChart-v3.ps1:94 char:87
+ ... nts[$curpoint].Label = "$($global:status[$key].'Failed Pings') Failed ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : PropertyNotFound
The property 'Color' cannot be found on this object. Verify that the property exists and can be set.
At C:\ToolBox\PINGMonitor\pingChart-v3.ps1:99 char:23
+ ...
$global:GoodChart.Series['Good'].points[$curpoint].Color ...
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : PropertyNotFound
Stopped the program and looked at the chart properties
$global:NoPingChart.Series['bad'].points[$curpoint]
XValue : 0
YValues : {150}
IsEmpty : False
Name : DataPoint
Label :
Then set the label manually
$global:NoPingChart.Series['bad'].points[$curpoint].label = "Test Label"
$global:NoPingChart.Series['bad'].points[$curpoint]
XValue : 0
YValues : {150}
IsEmpty : False
Name : DataPoint
Label : Test Label
As you can the label is there and I can change it manually.
I am hitting my head on my desk now trying to see what is going on.
Any help would be appreciated

System.Printing.Addjob() Error in Powershell

So I am trying to create a script that will take a print job from one paused print queue and add it to an active queue. However I am trying to utilize the AddJob() function and upon calling it with or without parameters it returns an exception and I am not sure why. Here is what I have so far
$host.Runspace.ThreadOptions = "ReuseThread"
Add-Type -AssemblyName System.Printing
$permissions = [System.Printing.PrintSystemDesiredAccess]::AdministrateServer
$queueperms = [System.Printing.PrintSystemDesiredAccess]::AdministratePrinter
$server = new-object System.Printing.PrintServer -argumentList $permissions
$queues = $server.GetPrintQueues(#([System.Printing.EnumeratedPrintQueueTypes]::Shared))
foreach ($q in $queues) {
if ($q.IsPaused -eq 1)
{
$qPaused = new-object System.Printing.PrintQueue -argumentList $server,$q.Name,1,$queueperms
}
else
{
$qPlaying = new-object System.Printing.PrintQueue -ArgumentList $server,$q.Name,2,$queueperms
}
}
$byteContents = #('This is a test')
$byteContents | Out-File -FilePath "C:\testinput.txt"
[byte[]]$bytes = Get-Content -Encoding byte -Path "C:\testinput.txt"
#$printJob = $qPaused.GetJob(3).
$qPlaying.AddJob()
$jobStream = $printJob.JobStream
$jobStream | Out-GridView
#$jobStream.Write($bytes, 0, $bytes.Length)
#$jobStream.Close()
What this gives me is an error at the $qPlaying.AddJob() saying
Exception calling "AddJob" with "0" argument(s): "Specified argument was out of the range of valid values.
Parameter name: clientPrintSchemaVersion"
At line:23 char:1
+ $qPlaying.AddJob()
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ArgumentOutOfRangeException
Thank you for any feedback.
The version of the Print Schema is defined when you call the constructor for the queue in this line:
$qPlaying = new-object System.Printing.PrintQueue -ArgumentList $server,$q.Name,2,$queueperms
You are using PrintQueue Constructor (PrintServer, String, Int32, PrintSystemDesiredAccess) where the Int32 is the Print Queue Schema version. The MSDN article has a remark that: "The Print Schema version released with Windows Vista is "1"." Which would make sense that when you use 2 and receive an out of range error that 2 isn't an acceptable value.
You could use 1 as the value or use an alternate constructor. For example:
$qPlaying = new-object System.Printing.PrintQueue -ArgumentList $server,$q.Name,$queueperms

Install windows update

I have following powershell script which should update my windows OS everytime I run it. Therefore I use the given windows API in order to search, download and install the updates. But somehow only searching for them actually works.
This is my script:
$global:scriptpath = $MyInvocation.MyCommand.Path
$global:dir = Split-Path $scriptpath
$global:logfile = "$dir\updatelog.txt"
write-host " Searching for updates..."
$session = New-Object -ComObject Microsoft.Update.Session
$searcher = $session.CreateUpdateSearcher()
$result = $searcher.Search("IsInstalled=0 and Type='Software' and IsHidden=0")
if ($result.Updates.Count -eq 0) {
Write-Host "No updates to install"
} else {
$result.Updates | Select Title
}
$downloads = New-Object -ComObject Microsoft.Update.UpdateColl
foreach ($update in $result){
try {
$update.AcceptEula()
$Null = $downloads.Add($update)
} catch {}
}
$count = $result.Updates.Count
write-host ""
write-host "There are $($count) updates available."
write-host ""
read-host "Press Enter to download\install updates"
$downloader = $session.CreateUpdateDownLoader()
$downloader.Updates = $downloads
$downloader.Download()
$installs = New-Object -ComObject Microsoft.Update.UpdateColl
foreach ($update in $result.Updates){
if ($update.IsDownloaded){
$installs.Add($update)
}
}
$installer = $session.CreateUpdateInstaller()
$installer.Updates = $installs
$installresult = $installer.Install()
$installresult
But I get following error:
Exception calling "Download" with "0" argument(s): "Exception from HRESULT: 0x80240024"
+ $downloader.Download()
+ ~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ComMethodTargetInvocation
Somehow this line: $downloader.Updates = $downloads is not executed, but I don't know why. I also already tried running the script as an admin, didn't work either.
That error code is the WU_E_NO_UPDATE, described here. Basically it says that the Updates collection is not set or empty.

Exception calling "StartWorkflow" with "3" argument(s): ""

I am trying to write a powershell script that kicks off two workflows.
One needs to be kicked off only once while the other needs to be run on every item in the list. However I am being plagued by an error and I don't know how to get rid of it. When I add a 4 argument I get the same error. I am running the script as an spshelladmin
Error
Exception calling "StartWorkflow" with "3" argument(s): ""
At C:\cert.ps1:26 char: 29
+ $em = $manager.StartWorkFlow <<<< ($items[0],$emails,$data)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
Code
$web = Get-SPWeb -Identity "http://portal.com/sites/it"
$manager = $web.Site.WorkFlowManager
$list = $web.Lists["Certificate Tracking"]
$assoc = $list.WorkflowAssociations.GetAssociationByName("Certificate Notification","en-US")
$assoc.AllowAsyncManualStart = $true
$assoc.AllowManual = $true
$emails = $list.WorkflowAssociations.GetAssociationByName("Status Update","en-US")
$emails.AllowAsyncManualStart = $true
$emails.AllowManual = $true
$view = $list.Views["All Items"] #All Items
$items = $list.GetItems($view)
$data = $assoc.AssociationData
$emData = $emails.AssociationData
$count = 0
foreach ($item in $items) {
$wf = $manager.StartWorkFlow($item,$assoc,$data)
}
$em = $manager.StartWorkFlow($items[0],$emails,$emData,$true)
$web.Dispose()
I actually discovered that a specific item I was targeting already had a workflow running on it, I did not place proper checks for the status (check out the comments on this code http://paulryan.com.au/2014/cancel-all-workflows/), very misleading error, I thought I had wrong overload with method call on StartWorkFlow ($item, $association, $data, $true), in SP 2013 there are 4 parameters not sure why the thread title says three... Anyways hope that helps someone.