MilestoneSystemsInc / PowerShellSamples

A collection of samples for managing your Milestone XProtect VMS using MilestonePSTools in PowerShell
https://www.milestonepstools.com
MIT License
36 stars 12 forks source link

Send-Alarm - only visible to admins. Users unable to see generated alarms. #117

Open davidfrankks opened 7 months ago

davidfrankks commented 7 months ago

When using the Send-Alarm function, the only people that can see the alarms are full admins.

https://www.milestonepstools.com/commands/en-US/Send-Alarm/#-alarm Example Code. Connect-ManagementServer -ServerAddress http://$MilestoneServerName -AcceptEula -basic -Credential $Credential -Force $CameraID = "26BE74E4-BC46-4637-A4F1-2C4668C61840" $Now = Get-date $CameraItem = Get-VmsCamera -Id $CameraID | Get-VmsVideoOSItem -Kind Camera $Alarm = New-Alarm -Message "AI Detections $Now" -Source $CameraItem -Description "test" -CustomTag "test" Send-alarm -Alarm $Alarm Disconnect-ManagementServer

As a full admin, i can see the alarms and the associated cameras. If i logon as a user with rights to see alarms, i can see the alarm count go up but cannot access the actual alarms and the alarm window is blank. If i grant that same user admin rights the alarms show up. I have tried granting all permissions short of full admin and no luck

This is being tested on a Prof+ 2022 R3 - Fully patched version. PSTools version is also latest.

image

Tarterman commented 7 months ago

@davidfrankks, I can reproduce this on my Corporate 2023 R3 system as well. I don't believe it is a PSTools think but is more likely an SDK thing (@joshooaj might be able to confirm that). It seems like a bug to me.

A way to work around it is to create an alarm that is triggered by a User-defined Event. Then, instead of using PSTools to trigger the alarm, you can use it to trigger the User-defined event and send the camera as metadata.

For example, I have a User-defined Event that is called Panic. I would create an alarm that looks like this: image

You could fill out the rest of the information on the alarm page as needed but don't assign anything in the "Related cameras" field.

Then, your PowerShell code could look like this:

$panicUDE = Get-UserDefinedEvent -Name "Panic"
$cam = Get-VmsCamera -Id '44e1d43d-4a54-4b8a-8e3d-513eb7007aac'
Send-UserDefinedEvent -UserDefinedEvent $panicUDE -Cameras $cam

That will trigger the alarm for anyone who has permissions to alarms and to the User-defined Event. If they don't have access to the camera they will still see the alarm but they won't see the video for the camera.

davidfrankks commented 7 months ago

I had already gone this path, its just unfortunate, because it now requires a UserDefineEvent and an Alarm tied to that.

With no way to pass custom data into the "title" like send-alarm has. So in our usage case, were passing motion data to an AI module and its determine if a certain object "Train Carrier is on the train tracks" and then sending an alarm with that object data included in the title. With send alarm we could say "BSNF On tracks, or CCR on Tracks" and the operators knew which control room that carrier was heading too based on the title.

In the UserDefinedEvent to Alarm scenario, if we have 10 train carriers i need 10 events and 10 alarms for the Alarm to contain the carrier name as I cannot pass that at runtime via code anymore.

Hopefully the MilestonePSTools author can shed some light.

davidfrankks commented 7 months ago

Not ideal but as a work around, you can get the alarm generated and change the Type in the database manually to System Alarm and it will show up after you refresh the console. So I agree, its definitely how the Send-Alert is creating alert that's the cause.

Dont do this in prod.

USE [Surveillance] GO UPDATE [Central].[Alarms] SET [Type] = 'System Alarm' WHERE [Type] is null GO

Tarterman commented 7 months ago

I generally direct people away from sending in alarms that aren't configured in the system because it bypasses the capability to manage those alarms (e.g., set default priority, associate a map, configure activation period, etc.). That being said, there are some cases where it can be beneficial but there is usually a different (and sometimes better) way.

For your scenario, you could do what you are trying to do without having to create an alarm and event for every train carrier. That could be done by using Analytic Events. Analytics Events can be triggered by sending a properly formatted XML string to the Event Server. Analytic Events have the ability to receive extra metadata about the same two tags that you can supply data on with New-Alarm along with two more tags:

These tags can be configured to be displayed in the Smart Client. So, when sending the Analytic Event, you could send the Train Carrier as one of the tags.

In order for Analytic Events to work, there are some things to configure first:

  1. In the Management Client, go to Tools -> Options -> Analytic Events
  2. Check the box for Enabled and take note of the port (or change it if desired). It defaults to 9090.
  3. For better security, select the "Specified network addresses" radio button and enter the address(es) or hostname(s) of the servers that will be sending the event to the Event Server. You can leave it on "All network addresses" if desired but it isn't as secure. It is useful for testing as well.
  4. Create an Analytic Event in the Management Client
  5. Create an alarm in the Management Client that is triggered by the Analytic Event and choose the camera(s) in the Sources section. Here is what my alarm looks like that is called Train and triggered by an Analytic Event called Train. image
  6. While still in the Management Client, select the "Alarm Data Settings" item under the Alarms section in the tree on the left.
  7. Click the "Alarm List Configuration" tab and move the column over that applies to the tag (Type, Location, Custom Tag, Vendor) that you intend to send the Train Carrier name in over to the "Selected columns" box and click Save.
  8. On the Alarm Manager tab in the Smart Client, right click any of the column headings in the Alarm List pane and select the tag the you moved over in step 7. If the Smart Client was logged in already when you did step 7, you'll need to log out and back in.

Now for the PowerShell part. Here is something I put together that will trigger an Analytic Event. You can enter the Train Carrier name in the Type variable, Location variable, Vendor variable, or Custom Tag variable.

$timestamp = Get-Date -Format "yyyy-MM-ddTHH:mm:ssK" # Example timestamp format: 2023-12-05T12:42:00-08:00
$message = "" # Required - has to match the name of the Analytic Event
$deviceGuid = '44e1d43d-4a54-4b8a-8e3d-513eb7007aac' # Required - should be the GUID of the camera
$description = "" # Optional - will show up in the "Instructions" box when you double-click the alarm in Smart Client
$type = "" # This is one of the four optional tags
$location = "" # This is one of the four optional tags
$vendor = "" # This is one of the four optional tags
$customTag = "" # This is one of the four optional tags
$eventServer = "event_server_hostname_or_ip_address" # Required
$eventServerPort = 9090 # Required - change if not using default port of 9090

$eventXml = "<?xml version='1.0' encoding='utf-8'?>
<AnalyticsEvent xmlns:i='http://www.w3.org/2001/XMLSchema-instance' xmlns='urn:milestone-systems'>
    <EventHeader>
        <ID>00000000-0000-0000-0000-000000000000</ID>
        <Timestamp>$($timestamp)</Timestamp>
        <Type>$($type)</Type>
        <Message>$($message)</Message>
        <CustomTag>$($customTag)</CustomTag>
        <Source>
            <FQID>
                <ObjectId>$($deviceGuid)</ObjectId>
            </FQID>
        </Source>
    </EventHeader>
    <Description>$($description)</Description>
    <Location>$($location)</Location>
    <Vendor>
        <Name>$($vendor)</Name>
    </Vendor>
</AnalyticsEvent>"

$tcpClient = New-Object System.Net.Sockets.TCPClient
$tcpClient.Connect($eventServer,$eventServerPort)

[byte[]]$bytes  = [text.Encoding]::Ascii.GetBytes($eventXml)
$clientStream = $tcpClient.GetStream()

$clientStream.Write($bytes,0,$bytes.length)
$clientStream.Flush()

$tcpClient.Close()
$clientStream.Close()

Here is an example of me triggering the same Analytic Event but setting the Vendor tag as BNSF for one and CCR for the other. The Analytic Event was called "Trains" and the alarm was named "Train on Tracks".
image

This shows that if I click the "filter" option above the alarms then I can filter for these custom tags also. image

davidfrankks commented 7 months ago

This is indeed a eloquent solution. I appreciate your time and input. We will integrate this into our code base as it successfully separates the alerts, allowing operators to concentrate solely on the requirements of their control room. I'm hopeful that future versions of PSTools will enhance Send-Alarm to enable users to view the alarms it creates.

Thank You.

joshooaj commented 4 months ago

Just wanted to add that I looked into this the other day and there's an "AssignedTo" property on the alarm object but it's a composed string like "AccountName (domain\AccountName)" and no matter what I set it to, it isn't assigned properly.

I'll pass a question on to the MIP devs to see if this is possible with current APIs.