[{"server":null,"owner":null,"id":"7fc0205a-bd7c-4a02-8434-800c94e55284","params":{"result":{"value":"com.integra.selector_subscriptions.f4abdd0e727d41b98ebc426d625d10fc","datetime":1587708208,"status":null,"version":0},"hash":{"value":"VyD76+BanPv9iwC1hm2Wew==","datetime":1587708208,"status":null,"version":0},"enabled":{"value":true,"datetime":1587708208,"status":null,"version":0},"error_text":{"value":null,"datetime":1587708208,"status":null,"version":0},"type":{"value":"EgsScenario","datetime":1587708208,"status":null,"version":0},"settings":{"value":"{\r\n  \"main_loop_interval\": \"00:00:01\",\r\n  \"reset_param_timeout\": \"00:00:05\",\r\n  \"turn_off_StreetLighting_timeout\": \"00:00:30\",\r\n  \"reset_command_timeout\": \"00:00:05\",\r\n  \"max_command_repeats\": 10,\r\n  \"state_event_reasons\": {\r\n    \"Zone\": [\r\n      \"alarm_*\"\r\n    ],\r\n    \"VideoCamera\": [\r\n      \"alarm_fixObjectOn\",\r\n      \"alarm_objectClassFound\",\r\n      \"alarm_vehicleMoveInProhibitedDirection\",\r\n      \"alarm_motionInZone\",\r\n      \"alarm_stopVehicleInProtectedZone\"\r\n    ],\r\n    \"StilsoftLowCurrentVolumeSensor\": [\r\n      \"alarm_*\"\r\n    ],\r\n    \"StilsoftLowCurrentContactSensor\": [\r\n      \"alarm_*\"\r\n    ]\r\n  },\r\n  \"state_event_timeout\": \"00:01:00\",\r\n  \"play_siren\": {\r\n    \"reasons\": {\r\n      \"BOLID_FireButton\": {\r\n        \"alarm_*\": \"2\",\r\n        \"error_*\": \"3\"\r\n      }\r\n    },\r\n    \"paths\": [\r\n      \"Link/Space/Link/LowCurrentSirenMeta\",\r\n      \"Link/Zone/Link/LowCurrentSirenMeta\"\r\n    ]\r\n  }\r\n}","datetime":1587708263,"status":null,"version":0},"state":{"value":"ok.normal","datetime":1587708208,"status":null,"version":0},"script":{"value":"# имя: 'Misc Kerch'\r\n# описание: управление прожекторами для керченского моста\r\n# тип триггера: 'EgsScenario'\r\n# создан: 2017.04.24 15.55.51, Сельченков Н.Ю.\r\n# изменен: '2020.04.24 09.36.20', Сельченков Н.Ю.\r\n# подробности: https://redmine.integra-s.com:11000/projects/eilyacuario/wiki/Misc_Kerch\r\n\r\n### UTILS ###\r\n\r\nuse Newtonsoft.Json.JsonConvert from Newtonsoft.Json\r\nuse acuario2.utils.TextExtension\r\n\r\nlet json(obj as object) = JsonConvert.SerializeObject(obj)     \r\nlet shrink(str as string, threshold as int) = TextExtension.Shrink(str ?? \"\", threshold)\r\n\r\n### SETTINGS ###\r\n\r\nuse json_schema\r\n`\r\n{\r\n    \"type\": \"object\",\r\n    \"properties\":\r\n    {\r\n        \"main_loop_interval\": { \"type\": \"string\", \"format\": \"TimeSpan\", \"default\": \"00:00:01\" },\r\n        \"reset_command_timeout\": { \"type\": \"string\", \"format\": \"TimeSpan\", \"default\": \"00:00:05\" },\r\n        \"max_command_repeats\": { \"type\": \"integer\", \"default\": 10 },\r\n        \"turn_off_StreetLighting_timeout\": { \"type\": \"string\", \"format\": \"TimeSpan\", \"default\": \"00:00:30\" },\r\n        \"state_event_timeout\": { \"type\": \"string\", \"format\": \"TimeSpan\", \"default\": \"00:00:30\" },\r\n        \"state_event_reasons\":      \r\n        { \r\n            \"type\": \"object\",\r\n            \"additionalProperties\": \r\n            { \r\n                \"type\": \"array\",\r\n                \"items\": { \"type\": \"string\" }\r\n            }\r\n        },        \r\n        \"play_siren\":      \r\n        { \r\n            \"type\": \"object\",\r\n            \"properties\":\r\n            {\r\n                \"paths\": \r\n                { \r\n                    \"type\": \"array\",\r\n                    \"items\": { \"type\": \"string\" }\r\n                     \r\n                },\r\n                \"reasons\":      \r\n                { \r\n                    \"type\": \"object\",\r\n                    \"additionalProperties\": \r\n                    { \r\n                        \"type\": \"object\",\r\n                        \"additionalProperties\": { \"type\": \"string\" } \r\n                    }\r\n                }         \r\n            }\r\n        }         \r\n    }\r\n}\r\n` as SETTINGS\r\n\r\nlet settings = SETTINGS(this.settings)\r\n\r\n### COMMANDS ###\r\n\r\nuse new { command = \"\", counter = 0 } as CommandCounter\r\nuse System.Collections.Generic.Dictionary(Guid, CommandCounter) as CommandCounterDictionary\r\n\r\nlet cmds = CommandCounterDictionary()\r\n\r\nlet set_command(item, command) = \r\n    if item.Id isnt in cmds then cmds[item.Id] = CommandCounter()\r\n    let cmd = cmds[item.Id]\r\n    if string(command) is cmd.command then \r\n        cmd.counter = cmd.counter + 1\r\n    else \r\n        cmd.command = string(command)\r\n        cmd.counter = 0 \r\n\r\n    if cmd.counter <= settings.max_command_repeats then \r\n        let timeout = settings.reset_command_timeout\r\n        if item.command isnt command then item.command = command\r\n        else if item[\"command\"].TimePassed > timeout then item[\"command\"].Set(command, null, DateTime.UtcNow)\r\nend\r\n\r\nlet inside_alarmed_zone(obj as BaseObject) = from obj.GetItemsLinkedTo(ZoneInputPin) of type Zone any it.State is \"alarm\"\r\n\r\nlet get_alarmed_zone_containing_sibling_lamp(lamp as StreetLighting) =\r\n    from   lamp.GetItemsLinkedTo(LogicInput)   of type StilSoftRelay\r\n    select many GetItemsLinkedTo(LogicOutput)  of type StreetLighting\r\n    select many GetItemsLinkedTo(ZoneInputPin) of type Zone\r\n    where it.State is \"alarm\" try first\r\nend\r\n\r\nlet outoffix (lamp as StreetLighting) = lamp.State is \"error\"\r\nlet active   (lamp as StreetLighting) = lamp.state is \"ok_active\"\r\nlet elapsed  (lamp as StreetLighting) = (lamp is active) and (lamp[\"state\"].TimePassed > settings.turn_off_StreetLighting_timeout)\r\n\r\nlet turn_on_StreetLighting() =\r\n    from graph.Values of type BaseObject where it.State is \"alarm\" do\r\n        let alarmed_obj = it\r\n        from GetItemsLinkedTo(ZoneInputPin) of type Zone\r\n        select many GetItemsLinkedTo(ZoneOutputPin) of type StreetLighting \r\n        where (it isnt outoffix) and (it isnt active) do\r\n            set_command(it, RelaySwitchCommand.turn_on)\r\n            additional_info = \"turn_on, \"..state..\", \"..alarmed_obj\r\n        now\r\n    now\r\nend\r\n\r\nlet turn_off_StreetLighting() =     \r\n    from graph.Values of type StreetLighting \r\n    where (it isnt outoffix) and (it is elapsed) do\r\n        let alarmed_zone = get_alarmed_zone_containing_sibling_lamp(it)\r\n        if alarmed_zone is null then \r\n            set_command(it, RelaySwitchCommand.turn_off)\r\n            additional_info = \"turn_off, \"..state..\", \"..it[\"state\"].DateTime\r\n        else\r\n            additional_info = \"keep, \"..state..\", \"..alarmed_zone\r\n    now\r\nend\r\n\r\nlet on_StreetLighting_command(lamp as StreetLighting) = \r\n    from lamp.GetItemsLinkedTo(LogicInput) of type StilSoftRelay do\r\n        switch lamp.command\r\n            when \"turn_on\"  then set_command(it, Stilsoft_relaySwitchCommand.turn_on)\r\n            when \"turn_off\" then set_command(it, Stilsoft_relaySwitchCommand.turn_off)\r\n    now\r\n    lamp.command = \"DEFAULT\"\r\nend\r\n\r\nlet command_from_StreetLighting() = from graph.Values of type StreetLighting do on_StreetLighting_command(it) now\r\n\r\nlet on_StilSoftRelay_state(relay as StilSoftRelay) = \r\n    from relay.GetItemsLinkedTo(LogicOutput) of type StreetLighting do\r\n        switch relay.state\r\n            when \"ok_true\"         then state = \"ok_active\"\r\n            when \"ok_false\"        then state = \"ok_inactive\"\r\n            when \"error_defective\" then state = \"error_defective\"\r\n            else                        state = \"none_unknown\"     \r\n    now\r\nend\r\n\r\nlet state_from_StilSoftRelay() = from graph.Values of type StilSoftRelay do on_StilSoftRelay_state(it) now\r\n\r\nlet on_timer() = \r\n    turn_on_StreetLighting()\r\n    turn_off_StreetLighting()\r\n    command_from_StreetLighting()\r\n    state_from_StilSoftRelay()\r\nend\r\nthis.RunOnTimer(settings.main_loop_interval.TotalSeconds, on_timer) \r\n\r\n### EVENTS ###\r\n\r\nuse System.ObservableExtensions from System.Reactive.Core\r\nuse acuario2.client.StateEvent as StateEventImpl\r\n\r\nuse (\"acuario2.client.Protocol+PutResponse\")               as PutResponse      from acuario2.client \r\nuse (\"acuario2.client.Protocol+OpenTopicRequest\")          as OpenTopicRequest from acuario2.client \r\nuse (\"acuario2.client.Protocol+OpenTopicRequest+Selector\") as Selector         from acuario2.client \r\n\r\nconst PutResponseArray = PutResponse[] as Type\r\nuse System.Action(PutResponseArray) as OnEvent\r\n\r\nlet channel    = this.Module.GetWampChannel(this.ServerId ?? Guid.Empty) \r\nlet rpcCatalog = channel.RealmProxy.RpcCatalog\r\nlet services   = channel.RealmProxy.Services \r\n\r\nlet getSubject =  \r\n    from services.GetType().GetMethods() \r\n    where Name is \"GetSubject\"\r\n    where GetParameters().Length is 1\r\n    where IsGenericMethodDefinition \r\n    select MakeGenericMethod(new [PutResponseArray]) single\r\nend    \r\n\r\nlet subscribe =  \r\n    from (ObservableExtensions as Type).GetMethods() \r\n    where Name is \"Subscribe\"\r\n    where GetParameters().Length is 2 \r\n    where IsGenericMethodDefinition \r\n    select MakeGenericMethod(new [PutResponseArray])\r\n    where GetParameters()[1].ParameterType.IsAssignableFrom(OnEvent as Type) single\r\nend    \r\n\r\nlet subscribe_to_events(action as OnEvent) =\r\n    try await Protocol.CloseTopic(rpcCatalog, this.result ?? \"\")\r\n    this.result       = null\r\n    let selector      = Selector()\r\n    selector.type     = \"PropertyEqualitySelector\" \r\n    selector.name     = \"entity\";\r\n    selector.value    = \"event\" \r\n    let request       = OpenTopicRequest()\r\n    request.type      = \"OrCompositeSelector\"\r\n    request.selectors = from (new [selector]) select it to list\r\n    this.result       = await Protocol.OpenTopic(rpcCatalog, request)\r\n    let subject       = getSubject.Invoke(services, new [this.result as object])\r\n    subscribe.Invoke(null, new [subject, action])\r\nend\r\n    \r\nuse System.Collections.Concurrent.ConcurrentDictionary(string, StateEvent) as StateEventHistory\r\n\r\nlet events = StateEventHistory()\r\n\r\nlet same_as(event1 as StateEvent, event2 as StateEvent) = \r\n    (event1.owner is event2.owner) and (event1.state is event2.state) \r\nend    \r\n\r\nlet already_remembered(event as StateEvent) = \r\n    let same = from events.Values where it is same_as event try first \r\n    if same isnt null then print(\"ALREADY SENT\", string(same.Id), same.owner, same.name, same.state, same.datetime)\r\n    same isnt null\r\nend\r\n\r\nlet remember_event(guid as string, event as StateEvent) = \r\n    if events.TryAdd(guid, event) then\r\n        print(\"REMEMBER EVENT\", guid, event.owner, event.name, event.state, event.datetime)\r\nend    \r\n\r\nlet forget_event(guid as string) =\r\n    let event = null as StateEvent\r\n    if events.TryRemove(guid, event) then\r\n        print(\"FORGET EVENT\", guid, event.owner, event.name, event.state, event.datetime)\r\nend\r\n\r\nlet old(event as StateEvent) =\r\n    DateTime.UtcNow - event.datetime > settings.state_event_timeout\r\nend\r\n\r\nlet forget_old_events() =\r\n    let guids = from events where Value is old select Key to array\r\n    from guids do forget_event(it) now\r\nend\r\n\r\nlet send_event(obj as BaseObject) = \r\n    print(\"TRY SENDING EVENT BECAUSE OF\", obj.Id, obj, \"state = \", obj[\"state\"].Text)\r\n    \r\n    forget_old_events()\r\n        \r\n    let event        = StateEventImpl()\r\n    event.owner      = string(obj.Id)\r\n    event.server     = string(obj.ServerId)\r\n    event.position   = obj.position\r\n    event.state      = obj[\"state\"].Text?.Replace?('_', '.')\r\n    event.datetime   = obj[\"state\"].DateTime\r\n    event.version    = obj.Version\r\n    event.itemtype   = obj.GetType().Name\r\n    event.name       = obj.name\r\n    event.alarm_info = (obj as Zone)?.alarm_info\r\n        \r\n    if event isnt already_remembered then\r\n        print(\"SEND EVENT\", Guid.Empty, event.owner, event.name, event.state, event.datetime)\r\n        let request  = event.ExportCreate(this.ServerId)\r\n        let channel  = this.Module.GetWampChannel(this.ServerId ?? Guid.Empty)\r\n        Protocol.Put(channel.RealmProxy.RpcCatalog, request)\r\n        remember_event(string(event.Id), event)\r\n    end\r\nend        \r\n\r\nlet try_send_event(obj as BaseObject) = \r\n    let state = obj[\"state\"].Text \r\n    from settings.state_event_reasons where Key in obj.Types \r\n    select many (from Value where state like it) take 1 do \r\n        send_event(obj)\r\n    now\r\nend\r\n\r\nlet event_status_update(put as PutResponse) = \r\n    (put.entity is \"event\") and (put.type is \"StateEvent\") and (put.name is \"event_status\")\r\nend\r\n\r\nlet on_event(puts as PutResponseArray) =\r\n#    print(json(puts))\r\n\r\n    from puts where it is event_status_update do \r\n        print(\"EVENT STATUS UPDATE\", id, value)\r\n        forget_event(id) \r\n    now\r\n    \r\n    void()\r\nend\r\n\r\nasync subscribe_to_events(on_event)\r\n\r\n### SIREN ###\r\n\r\nlet try_play_siren(obj as BaseObject) = \r\n    let state = obj[\"state\"].Text \r\n    from settings.play_siren.reasons where Key in obj.Types \r\n    select many (from Value where state like Key select Value) take 1 do \r\n        let fileId = it\r\n        from settings.play_siren.paths \r\n        select many graph.Find(obj, it) of type LowCurrentSirenMeta do\r\n            it.fileId  = fileId\r\n            it.command = \"start\"\r\n        now\r\n    now\r\nend\r\n\r\n### UPDATES ###\r\n\r\nlet on_update(source as AbstractObject, changes as string[]) = \r\n    from changes do print(\"PARAM UPDATE\", (source as Item)?.Id, source, it, \"=\", shrink(source[it].Text, 10)) now \r\n\r\n    if source is StreetLighting lamp then\r\n        if \"command\" in changes then\r\n            on_StreetLighting_command(lamp)\r\n\r\n    if source is StilSoftRelay relay then\r\n        if \"state\" in changes then\r\n            on_StilSoftRelay_state(relay)\r\n        \r\n    if source is BaseObject obj then\r\n        if \"state\" in changes then\r\n            try_send_event(obj) \r\n            try_play_siren(obj)\r\n                \r\n    void()\r\nend\r\nthis.RunOnUpdated(on_update)\r\n","datetime":1587708208,"status":null,"version":0},"name":{"value":"Misc Kerch (OLD)","datetime":1587708223,"status":null,"version":0}},"entity":"item","operation":"create"}]