# имя: 'Trace detector 2' # описание: детектор слежения за целями в зоне # тип триггера: 'EgsScheduled' # создан: 2017.04.24 15.55.51, Сельченков Н.Ю. # изменен: '2018.12.21 11.38.12', Сельченков Н.Ю. # подробности: https://redmine.integra-s.com:11000/projects/eilyacuario/wiki/Trace_detector_2 use System.Math use acuario2.types.BaseObject from acuario2.types use acuario2.types.Zone from acuario2.types use acuario2.types.PTZDevice from acuario2.types use acuario2.types.MoveableObject from acuario2.types use acuario2.types.UndefinedTarget from acuario2.types use acuario2.types.Ship from acuario2.types use acuario2.types.Aircraft from acuario2.types use acuario2.types.ZoneAction from acuario2.types use acuario2.client.ZoneEvent from acuario2.types use acuario2.types.BaseEvent from acuario2.client use acuario2.client.Protocol from acuario2.client use acuario2.utils.DateTimeExtension use utils.GeoPoint from GeoUtils use utils.GeoUtils from GeoUtils use acuario2.types.TraceDetectorSettings from acuario2.client use System.Collections.Generic.List(GeoPoint) as GeoPointList use System.Collections.Generic.List(ZoneEvent) as ZoneEventList use new { ready = false, speed = 0.0, heading = 0.0, moving = true, too_fast = false, approached = false } as TargetContext use System.Collections.Generic.Dictionary(Guid, TargetContext) as ZoneContext use System.Collections.Generic.Dictionary(Guid, ZoneContext) as Context use typedef ` 500 300 240 180 60 20 ` as Settings const settings = Settings(trigger.settings) const spawn_alarm_types = settings.spawn_alarm_types ?? string[](0) const trace_highest_types = settings.trace_highest_types ?? string[](0) const trace_high_types = settings.trace_high_types ?? string[](0) const trace_normal_types = settings.trace_normal_types ?? string[](0) const trace_low_types = settings.trace_low_types ?? string[](0) const trace_lowest_types = settings.trace_lowest_types ?? string[](0) const trace_timeout = settings.trace_timeout ?? 500 const trace_highest_timeout = settings.trace_highest_timeout ?? 300 const trace_high_timeout = settings.trace_high_timeout ?? 240 const trace_normal_timeout = settings.trace_normal_timeout ?? 180 const trace_low_timeout = settings.trace_low_timeout ?? 120 const trace_lowest_timeout = settings.trace_lowest_timeout ?? 60 const trace_types = from trace_highest_types union trace_high_types union trace_normal_types union trace_low_types union trace_lowest_types to array let rpcCatalog = trigger.Module.WampChannel.RealmProxy.RpcCatalog const validType(typename as string) = Type.GetType("acuario2.types."..typename..",acuario2.types") isnt null eval(from trace_types where it isnt validType do print "type "..it.." not found" now) assert(from trace_types all it is validType, "invalid object type name") once context = null as Context let events = ZoneEventList() let unix_now() = DateTimeExtension.ToUnixTime(DateTime.UtcNow) let seconds_passed(datetime as DateTime) = unix_now() - DateTimeExtension.ToUnixTime(datetime) let get_zone(id as Guid) = from trigger.Module.Graphs select it[id] of type Zone single let get_target(id as Guid) = if id in graph then graph[id] as MoveableObject let get_zone_ctx(id as Guid) = if not context.ContainsKey(id) then context[id] = ZoneContext() context[id] end let get_target_ctx(zone_ctx as ZoneContext, id as Guid) = if zone_ctx isnt null then if not zone_ctx.ContainsKey(id) then zone_ctx[id] = TargetContext() zone_ctx[id] end end if context is null then print "initializing zones context..." context = Context() from graph.Values of type MoveableObject where trace_zones isnt empty do let target = it from trace_zones do get_target_ctx(get_zone_ctx(Guid(it)), target.Id) now end now end let fill_event(event as ZoneEvent, zone as Zone, target as MoveableObject) = event.owner = string(zone.Id) event.server = string(target.ServerId) event.position = target.position event.detector = string(trigger.Id) event.target = string(target.Id) event.target_type = target.GetType().Name event.target_info = switch target when Ship then with target as Ship do iMO.." "..mMSI.." "..name else target.name end let intersected_with(seq1, seq2) = from seq1 intersect seq2 count all > 0 let process_target(zone as Zone, target as MoveableObject, enter as bool) = print(if enter then "ENTER" else "EXIT", zone, target) if target.trace_zones is null then target.trace_zones = string[](0) if enter then target.trace_zones = from target.trace_zones union string(zone.Id) to array else target.trace_zones = from target.trace_zones except string(zone.Id) to array if target.trace_priority isnt "ignore" then let target_type = target.GetType().Name if target.trace_priority is "DEFAULT" then target.trace_priority = switch target_type when in trace_highest_types then "highest" when in trace_high_types then "high" when in trace_normal_types then "normal" when in trace_low_types then "low" when in trace_lowest_types then "lowest" else switch target.Types when intersected_with trace_highest_types then "highest" when intersected_with trace_high_types then "high" when intersected_with trace_normal_types then "normal" when intersected_with trace_low_types then "low" when intersected_with trace_lowest_types then "lowest" else "DEFAULT" end let event = ZoneEvent() fill_event(event, zone, target) event.alarm = target_type in spawn_alarm_types event.action = if enter then "enter" else "exit" events.Add(event) end end let process_zone(zone as Zone) = let result = await Protocol.GetObjectsByPoligon(rpcCatalog, graph.ServerId, trace_types, zone.area, trace_timeout) let zone_ctx = get_zone_ctx(zone.Id) let exited = from zone_ctx.Keys except result to array let entered = from result except zone_ctx.Keys to array from exited do zone_ctx.Remove(it) select get_target(it) of type MoveableObject do process_target(zone, it, false) now from entered do get_target_ctx(zone_ctx, it) select get_target(it) of type MoveableObject do process_target(zone, it, true) now end from trigger.Module.Graphs select many (from Values of type Zone) where trace_priority isnt "ignore" do process_zone(it) now from graph.Values of type MoveableObject where (trace_priority isnt "ignore") and (trace_priority isnt "DEFAULT") do let timeout = switch trace_priority when "highest" then trace_highest_timeout when "high" then trace_high_timeout when "normal" then trace_normal_timeout when "low" then trace_low_timeout when "lowest" then trace_lowest_timeout if seconds_passed(it["position"].DateTime) > timeout then trace_priority = "DEFAULT" now let check_target(zone as Zone, polygon as GeoPointList, zone_ctx as ZoneContext, target as MoveableObject, target_ctx as TargetContext, settings as TraceDetectorSettings) = let add_event(alarm as bool?, action as ZoneAction, value_before as double, value_after as double, participants as Guid[]) = let event = ZoneEvent() event.alarm = alarm ?? false event.action = action event.value_before = string(value_before) event.value_after = string(value_after) event.participants = if participants is empty then null else string[](participants) fill_event(event, zone, target) if target.trace_priority isnt "ignore" then events.Add(event) end let get_geo_point(obj as MoveableObject) = let p = GeoPoint((obj as BaseObject).position) p.Height = GeoUtils.GetZoneApproxHeight(polygon, p) p end let point = get_geo_point(target) if target_ctx.ready then let moving = target.speed > settings.stop_threshold if moving isnt target_ctx.moving then add_event(settings.stop_alarm, if moving then "start" else "stop", target_ctx.speed, target.speed, null) target_ctx.moving = moving else if moving then let too_fast = target.speed > settings.speed_limit if too_fast isnt target_ctx.too_fast then add_event(settings.speed_limit_alarm, if too_fast then "too_fast" else "not_too_fast", target_ctx.speed, target.speed, null) target_ctx.too_fast = too_fast end let delta = Math.Abs(target.speed - target_ctx.speed) if delta > settings.speed_change_threshold then add_event(settings.speed_change_alarm, "severe_speed_change", target_ctx.speed, target.speed, null) end let delta = Math.Abs(target.heading - target_ctx.heading) if delta > settings.heading_change_threshold then add_event(settings.heading_change_alarm, "severe_heading_change", target_ctx.heading, target.heading, null) end end let participants = from zone_ctx select get_target(Key) of type MoveableObject where (it isnt target) and (point.Distance(get_geo_point(it)) < settings.approach_threshold) order by Id select Id to array let approached = participants isnt empty if approached isnt target_ctx.approached then add_event(settings.approach_alarm, if approached then "dangerous_approach" else "no_dangerous_approach", 0, 0, participants) target_ctx.approached = approached end end target_ctx.speed = target.speed target_ctx.heading = target.heading target_ctx.ready = true end let find_settings(zone as Zone, target as MoveableObject) = let type = target.GetType().Name let guid = string(target.Id) let settings = zone.GetLinkedItems(TraceDetectorSettings) from settings where (guids isnt null) and (guid in guids) concat ( from settings where (types isnt null) and (type in types) ) concat ( from settings where (types is empty ) and (guids is empty) ) try first end from context do let zone = get_zone(Key) let polygon = GeoUtils.ParsePolygon(zone.area, zone.area_heights) let zone_ctx = Value from zone_ctx do let target = get_target(Key) if target isnt null then let settings = find_settings(zone, target) if settings isnt null then check_target(zone, polygon, zone_ctx, target, Value, settings) end now now if events isnt empty then let request = from events select many ExportCreate(Guid(server)) to array Protocol.Put(rpcCatalog, request) end