private async Task SetInitialControlState(TLCFIClientSession session, TLCFIClientStateManager stateManager) { _logger.Info("Setting initial application session state in TLC (with ControlState.Offline) if needed"); try { if (stateManager.ControlSession.ReqControlState != ControlState.Offline) { await session.SetReqControlStateAsync(ControlState.Offline); } session.State.Configured = true; _logger.Debug("Succesfully set initial application session state in TLC"); } catch (JsonRpcException e) { _logger.LogRpcException(e); throw new TLCFISessionException("Error while setting initial application control state in TLCFacilities."); } }
public TLCFIClientSessionJsonRpcHandler( TLCFIClientStateManager stateManager, TLCProxy tlcProxy, TLCFIClientSessionState sessionState, TwoWayTcpClient tcpClient, CancellationToken token) { _stateManager = stateManager; _tlcProxy = tlcProxy; _sessionCancellationToken = token; tcpClient.DataReceived += async(o, e) => { if (!_jsonRpcMethodRegex.IsMatch(e)) { return; } var result = _service?.HandleRpc(e); if (result != null) { await tcpClient.SendDataAsync(result, token); } }; _service = JsonRpcProcedureBinder.Default.GetInstanceService(this); }
public TLCFIClientSession(TLCFIClientStateManager stateManager, IPEndPoint ep, CancellationToken token) { _endPoint = ep; _sessionCancellationToken = token; _stateManager = stateManager; Client = new TwoWayTcpClient(); Client.DataSent += (o, e) => { DataSent?.Invoke(this, e); }; Client.Disconnected += Client_Disconnected; RemoteEndPoint = ep; State = new TLCFIClientSessionState(); TLCProxy = new TLCProxy(Client); _jsonRpcHandler = new TLCFIClientSessionJsonRpcHandler(stateManager, TLCProxy, State, Client, token); _jsonRpcHandler.ReceivedAlive += (o, a) => { _aliveReceivedTimer.Stop(); _aliveReceivedTimer.Start(); }; _jsonRpcHandler.UpdateStateCalled += OnUpdateStateCalled; _jsonRpcHandler.NotifyEventCalled += OnNotifyEventCalled; }
public async Task InitializeSession(TLCFIClientSession session, TLCFIClientConfig config, TLCFIClientStateManager stateManager, CancellationToken token) { if (config == null) { throw new NullReferenceException("Config is null, has SetConfig been called first?"); } try { var sessionId = await RegisterAsync(session, config, token); if (!session.State.Registered) { throw new TLCFISessionException("Registering with TLC failed"); } ApplicationRegistered?.Invoke(this, EventArgs.Empty); session.StartAliveTimers(); await GetSessionDataAsync(sessionId, session, config, stateManager, token); if (stateManager == null) { return; } await ReadFacilitiesMetaAsync(session, config, stateManager, token); Intersection inter = null; if (!config.UseIdsFromTLCForSubscription) { inter = await ReadIntersectionMetaAndSubscribeAsync(session, config, stateManager, token); } var refs = CollectAllRefs(stateManager.Facilities, inter, config); CheckMetaData(stateManager.Facilities, inter, config); await ReadAllObjectsMetaAsync(refs, session, config, stateManager, token); await SubscribeAllObjectsAsync(refs, session, stateManager, token); ApplicationConfigured?.Invoke(this, EventArgs.Empty); await SetInitialControlState(session, stateManager); _logger.Info("Client configured succesfully. Now ready to request control."); } catch (TLCFISessionException e) { _logger.Fatal(e, "Error initializing session. " + (e.Fatal ? "(FATAL!): " : ": ")); throw new TLCFISessionException("Error initializing session. " + (e.Fatal ? "(FATAL!) " : ""), e.Fatal); } }
private async Task SubscribeAllObjectsAsync(IEnumerable <ObjectReference> refs, TLCFIClientSession session, TLCFIClientStateManager stateManager, CancellationToken token) { try { var getstatetasks = (from objref in refs where objref.Ids.Length > 0 select session.TLCProxy.SubscribeAsync(objref, token)).Cast <Task>() .ToList(); await Task.WhenAll(getstatetasks.ToArray()); foreach (var t in getstatetasks) { using (var task = t as Task <ObjectData>) { if (task == null) { continue; } var data = task.Result; switch (data.Objects.Type) { case TLCObjectType.SignalGroup: for (var i = 0; i < data.Data.Length; ++i) { var sg = (SignalGroup)data.Data[i]; var ssg = stateManager.InternalSignalGroups.First(x => x.Id == data.Objects.Ids[i]); if (ssg == null) { throw new NullReferenceException(); } // copy state ssg.StateTicks = sg.StateTicks; ssg.State = sg.State; ssg.Predictions = sg.Predictions; } break; case TLCObjectType.Detector: for (var i = 0; i < data.Data.Length; ++i) { var d = (Detector)data.Data[i]; var sd = stateManager.InternalDetectors.First(x => x.Id == data.Objects.Ids[i]); if (sd == null) { throw new NullReferenceException(); } // copy state sd.StateTicks = d.StateTicks; sd.State = d.State; sd.FaultState = d.FaultState; sd.Swico = d.Swico; } break; case TLCObjectType.Input: for (var i = 0; i < data.Data.Length; ++i) { var ip = (Input)data.Data[i]; var sip = stateManager.InternalInputs.First(x => x.Id == data.Objects.Ids[i]); if (sip == null) { throw new NullReferenceException(); } // copy state sip.StateTicks = ip.StateTicks; sip.State = ip.State; sip.FaultState = ip.FaultState; sip.Swico = ip.Swico; } break; case TLCObjectType.Output: for (var i = 0; i < data.Data.Length; ++i) { var op = (Output)data.Data[i]; var sop = stateManager.InternalOutputs.First(x => x.Id == data.Objects.Ids[i]); if (sop == null) { throw new NullReferenceException(); } // copy state sop.StateTicks = op.StateTicks; sop.State = op.State; sop.FaultState = op.FaultState; } break; case TLCObjectType.Intersection: for (var i = 0; i < data.Data.Length; ++i) { var ins = (Intersection)data.Data[i]; var sins = stateManager.InternalIntersections.First(x => x.Id == data.Objects.Ids[i]); if (sins == null) { throw new NullReferenceException(); } // copy state sins.StateTicks = ins.StateTicks; sins.State = ins.State; session.State.IntersectionControl = sins.State == IntersectionControlState.Control; } break; case TLCObjectType.SpecialVehicleEventGenerator: if (data.Data.Length == 1) { var spv = (SpecialVehicleEventGenerator)data.Data[0]; var sspv = stateManager.SpvhGenerator; if (spv == null) { throw new NullReferenceException(); } // copy state sspv.FaultState = spv.FaultState; } break; case TLCObjectType.Variable: for (var i = 0; i < data.Data.Length; ++i) { var v = (Variable)data.Data[i]; var sv = stateManager.InternalVariables.First(x => x.Id == data.Objects.Ids[i]); if (sv == null) { throw new NullReferenceException(); } // copy state sv.Lifetime = v.Lifetime; sv.Value = v.Value; } break; case TLCObjectType.Session: // Already subscribed in Register() case TLCObjectType.TLCFacilities: // This object does not have a state throw new NotSupportedException(); default: throw new ArgumentOutOfRangeException(); } } } _logger.Info("Succesfully initialized TLC state in CLA"); } catch (JsonRpcException e) { _logger.LogRpcException(e); throw new TLCFISessionException("Error while subscribing to objects in TLCFacilities."); } catch (Exception e) { _logger.Error(e, "Error while obtaining objects from TLC"); throw new TLCFISessionException("Error while subscribing to objects in TLCFacilities."); } }
private async Task ReadAllObjectsMetaAsync(IEnumerable <ObjectReference> refs, TLCFIClientSession session, TLCFIClientConfig config, TLCFIClientStateManager stateManager, CancellationToken token) { if (!session.State.Registered) { _logger.Warn( "Error configuring application: not authorized with TLC; were Register() and ReadFacilitiesMeta() called?"); throw new TLCFISessionException("Client is authorized with TLC; were Register() and ReadFacilitiesMeta() called?"); } try { var getmetatasks = (from objref in refs where objref.Ids.Length > 0 select session.TLCProxy.ReadMetaAsync(objref, token)).Cast <Task>() .ToList(); await Task.WhenAll(getmetatasks.ToArray()); foreach (var t in getmetatasks) { using (var task = t as Task <ObjectMeta>) { if (task == null) { continue; } var meta = task.Result; switch (meta.Objects.Type) { case TLCObjectType.SignalGroup: foreach (SignalGroup sg in meta.Meta) { stateManager.InternalSignalGroups.Add(sg); } break; case TLCObjectType.Detector: foreach (Detector d in meta.Meta) { stateManager.InternalDetectors.Add(d); } break; case TLCObjectType.Input: foreach (Input ip in meta.Meta) { stateManager.InternalInputs.Add(ip); } break; case TLCObjectType.Output: foreach (Output op in meta.Meta) { stateManager.InternalOutputs.Add(op); } break; case TLCObjectType.SpecialVehicleEventGenerator: stateManager.SpvhGenerator = (SpecialVehicleEventGenerator)meta.Meta[0]; break; case TLCObjectType.Variable: foreach (Variable op in meta.Meta) { stateManager.InternalVariables.Add(op); } break; case TLCObjectType.Session: // Special kind of object; gets initialized in Register() case TLCObjectType.TLCFacilities: // Got initialized in ReadFacilitiesMeta() case TLCObjectType.Intersection: // Got initialized in ReadFacilitiesMeta() throw new NotSupportedException(); default: throw new ArgumentOutOfRangeException(); } } } } catch (JsonRpcException e) { _logger.LogRpcException(e); throw new TLCFISessionException("Error reading META from TLCFacilities."); } catch (Exception e) { _logger.Error(e, "Error while reading META data for all objects from TLC"); throw new TLCFISessionException("Error reading META from TLCFacilities."); } try { stateManager.Initialize(config.RemoteIntersectionId); // Check and init } catch (DuplicateNameException) { throw new TLCFISessionException("Error checking META data from TLCFacilities (FATAL!)", true); } }
private async Task <Intersection> ReadIntersectionMetaAndSubscribeAsync(TLCFIClientSession session, TLCFIClientConfig config, TLCFIClientStateManager stateManager, CancellationToken token) { ObjectMeta intersectionMeta = null; ObjectData intersectionState = null; var iref = new ObjectReference { Ids = new[] { config.RemoteIntersectionId }, Type = TLCObjectType.Intersection }; try { _logger.Info("Getting Intersection META data for intersection with id {0}", config.RemoteIntersectionId); intersectionMeta = await session.TLCProxy.ReadMetaAsync(iref, token); } catch (JsonRpcException e) { _logger.LogRpcException(e); } if (intersectionMeta == null || intersectionMeta.Meta.Length != 1) { var exmes = $"Error reading META of Intersection: received {intersectionMeta?.Meta.Length ?? 0} objects, expected 1"; _logger.Warn(exmes); throw new InvalidMetaReceivedException(exmes); } _logger.Info("Succesfully obtained Intersection META data"); var intersectionData = (Intersection)intersectionMeta.Meta[0]; stateManager.InternalIntersections.Add(intersectionData); try { intersectionState = await session.TLCProxy.SubscribeAsync(iref, token); } catch (JsonRpcException e) { _logger.LogRpcException(e); } if (intersectionState == null || intersectionState.Data.Length != 1) { var exmes = $"Error reading STATE of Intersection: received {intersectionState?.Data.Length ?? 0} objects, expected 1"; _logger.Warn(exmes); throw new InvalidMetaReceivedException(exmes); } var ins = (Intersection)intersectionState.Data[0]; var sins = stateManager.InternalIntersections.First(x => x.Id == intersectionState.Objects.Ids[0]); // Copy state sins.StateTicks = ins.StateTicks; sins.State = ins.State; session.State.IntersectionControl = sins.State == IntersectionControlState.Control; return(intersectionData); }
private async Task ReadFacilitiesMetaAsync(TLCFIClientSession session, TLCFIClientConfig config, TLCFIClientStateManager stateManager, CancellationToken token) { try { if (_facilitiesRef != null) { ObjectMeta facilitiesMeta = null; try { _logger.Info("Getting TLCFacilities META data"); facilitiesMeta = await session.TLCProxy.ReadMetaAsync(_facilitiesRef, token); } catch (JsonRpcException e) { _logger.LogRpcException(e); } if (facilitiesMeta != null && facilitiesMeta.Meta.Length == 1) { _logger.Info("Succesfully obtained TLCFacilities META data"); var facilitiesData = (TLCFacilities)facilitiesMeta.Meta[0]; stateManager.Facilities = facilitiesData; if (!facilitiesData.Intersections.Contains(config.RemoteIntersectionId)) { _logger.Error("Intersection with id {0} not found in TLCFacilities META data", config.RemoteIntersectionId); throw new TLCObjectNotFoundException(config.RemoteIntersectionId, TLCObjectType.Intersection); } } else { _logger.Fatal("Error reading META of TLCFacilities: received {0} objects, expected 1", facilitiesMeta?.Meta.Length ?? 0); throw new ArgumentOutOfRangeException(); } } else { _logger.Error( "Error reading META of TLCFacilities: reference to facilities is null; was Register() succesfully called?"); throw new NullReferenceException(); } } catch (Exception e) { _logger.Error(e, "Error reading META of TLCFacilities, canceling session; "); throw new TLCFISessionException("Error reading META of TLCFacilities, canceling session"); } }
private async Task <TLCFIClientStateManager> GetSessionDataAsync(string sessionId, TLCFIClientSession session, TLCFIClientConfig config, TLCFIClientStateManager stateManager, CancellationToken token) { _logger.Info("Obtaining session data and subscribing to session."); try { // Read meta for session var sref = new ObjectReference() { Ids = new[] { sessionId }, Type = TLCObjectType.Session }; var meta = await session.TLCProxy.ReadMetaAsync(sref, token); token.ThrowIfCancellationRequested(); if (meta == null || meta.Meta.Length != 1) { throw new InvalidMetaReceivedException("Incorrect response while reading session META. Meta received: " + meta); } // Check and store data, set state for session, and subscribe to session updates stateManager.Session = (TLCSessionBase)meta.Meta[0]; ValueChecker.CheckValidObjectId(stateManager.Session.Id); switch (config.ApplicationType) { case ApplicationType.Consumer: case ApplicationType.Provider: break; case ApplicationType.Control: if (stateManager.Session is ControlApplication ct) { // Start with Error state: either the TLC will set it to Offline, or we will in SetInitialControlState ct.ReqControlState = ControlState.Error; ct.StartCapability = config.StartCapability; ct.EndCapability = config.EndCapability; ct.ReqIntersection = config.RemoteIntersectionId; } break; default: throw new ArgumentOutOfRangeException(); } if (stateManager.Session.SessionType != config.ApplicationType) { throw new InvalidTLCObjectTypeException($"Type of Session (ApplicationType) incorrect. Expected {config.ApplicationType.ToString()}, got {stateManager.Session.SessionType.ToString()}"); } var data = await session.TLCProxy.SubscribeAsync(sref, token); token.ThrowIfCancellationRequested(); if (data == null || data.Data.Length != 1) { throw new InvalidStateReceivedException("Incorrect response while reading session STATE. State received: " + data); } ApplicationRegistered?.Invoke(this, EventArgs.Empty); _logger.Info("Succesfully got session meta and state: registered properly."); return(stateManager); } catch (JsonRpcException e) { session.State.Registered = false; _logger.LogRpcException(e); } catch (Exception e) { session.State.Registered = false; _logger.Info(e, "Register failed with non-jsonrpc error."); } return(null); }
public async Task <TLCFIClientSession> GetNewSession(IPEndPoint endPoint, TLCFIClientStateManager stateManager, CancellationToken token) { if (token.IsCancellationRequested) { return(null); } lock (_locker) { if (_activeSession != null && !_activeSession.Connected) { _logger.Warn("A session with {0}:{1} already exists, but is not connected. It will be disposed of.", _activeSession.RemoteEndPoint.Address, _activeSession.RemoteEndPoint.Port); DisposeActiveSession(); return(null); } if (_activeSession != null) { _logger.Warn("There already is a connected session with {0}:{1}. Simultaneous sessions are not allowed.", _activeSession.RemoteEndPoint.Address, _activeSession.RemoteEndPoint.Port); return(null); } } // Succesful registration interval if (DateTime.Now.Subtract(_lastSuccesfulRegister).TotalSeconds < 42) { var remaining = (int)(42 - DateTime.Now.Subtract(_lastSuccesfulRegister).TotalSeconds) + 1; _logger.Info("Need 42 seconds between succesful register calls. Still need to wait {0} seconds. ", remaining); await Task.Run(async() => { while (remaining > 0) { await Task.Delay(1000, token); remaining--; if (remaining > 0) { _logger.Trace("Starting new session in {0} seconds.", remaining); } } }, token); } _tokenSource = new CancellationTokenSource(); var session = new TLCFIClientSession(stateManager, endPoint, _tokenSource.Token); _activeSession = session; session.SessionEnded += OnSessionEnded; session.Disconnected += OnSessionDisconnected; session.ControlStateSetToError += OnSessionControlStateSetToError; session.ReceiveAliveTimeoutOccured += OnSessionReceiveAliveTimeout; session.EventOccured += OnSessionEventOccured; var watch = new Stopwatch(); watch.Reset(); var sesIp = endPoint.Address.ToString(); var sesPort = endPoint.Port.ToString(); while (!session.Connected) { try { var remaining = _timeout - watch.ElapsedMilliseconds; if (remaining > 0) { await Task.Delay((int)remaining, token); } _tries++; // Backoff procedure if (_tries >= 25) { _timeout = 60000; } else if (_tries >= 21) { _timeout = 30000; } else if (_tries >= 10) { _timeout = 5000; } else if (_tries >= 5) { _timeout = 2000; } watch.Reset(); watch.Start(); _logger.Info("Connecting to {0}:{1}, try {2}", sesIp, sesPort, _tries); ConnectingStarted?.Invoke(this, _tries); if (token.IsCancellationRequested || _tokenSource.IsCancellationRequested) { return(null); } await session.StartSessionAsync(_timeout); } catch (TaskCanceledException) { return(null); } catch { _logger.Warn("Connecting to {0}:{1} try {2} failed", sesIp, sesPort, _tries); ConnectingFailed?.Invoke(this, _tries); } } _logger.Info("TCP session with {0}:{1} started", sesIp, sesPort); TLCSessionStarted?.Invoke(this, session); return(session); }
private void OnUpdateStateCalled(object sender, ObjectStateUpdate objectstateupdate) { switch (objectstateupdate.Objects.Type) { case TLCObjectType.Session: if (objectstateupdate.Objects.Ids.Length == 1 && objectstateupdate.Objects.Ids[0] == _stateManager.Session.Id && objectstateupdate.States.Length == 1) { var application = (ControlApplication)objectstateupdate.States[0]; if (application == null) { throw new JsonRpcException((int)ProtocolErrorCode.InvalidObjectReference, "State could not be properly cast to type ControlApplication (object type: TLCObjectType.Session).", null); } if (!application.ControlState.HasValue) { throw new JsonRpcException((int)ProtocolErrorCode.InvalidAttributeValue, "ControlState was not set on ControlApplication object.", null); } _logger.Info("TLC set Application.ControlState from {0} to {1}.", _stateManager.ControlSession.ControlState, application.ControlState); if (_stateManager.ControlSession.ControlState.HasValue) { if (!TLCFIStateChecker.IsControlStateChangeOk(_stateManager.ControlSession.ControlState.Value, application.ControlState.Value)) { _logger.Warn("Invalid ControlState transition made by TLC: from {0} to {1}. Resulting behaviour is undefined.", _stateManager.ControlSession.ControlState, application.ControlState); } } switch (application.ControlState) { case ControlState.Error: State.SessionControl = false; _logger.Error("TLC set Application.ControlState to ControlState.Error."); ControlStateSetToError?.Invoke(this, EventArgs.Empty); break; case ControlState.NotConfigured: State.SessionControl = false; // During startup, accept transition from 0 (error) to NotConfigured if (_stateManager.ControlSession.ReqControlState == ControlState.Error) { _logger.Info("Will confirm ControlState by setting requested state to ControlState.NotConfigured."); Task.Run(() => SetReqControlStateAsync(ControlState.NotConfigured), _sessionCancellationToken); } // Otherwise, if not requested, this transition is false else if (_stateManager.ControlSession.ReqControlState != ControlState.NotConfigured) { _logger.Warn("TLC set Application.ControlState to ControlState.NotConfigured. " + "(Requested = {0}).", _stateManager.ControlSession.ReqControlState); } break; case ControlState.Offline: State.SessionControl = false; // Accept Offline during startup if ((_stateManager.ControlSession.ReqControlState == ControlState.Error || _stateManager.ControlSession.ReqControlState == ControlState.NotConfigured) && _stateManager.ControlSession.ControlState == ControlState.Offline) { _logger.Info("Will confirm ControlState by setting requested state from ControlState.NotConfigured to ControlState.Offline."); Task.Run(() => SetReqControlStateAsync(ControlState.Offline), _sessionCancellationToken); } // Log Offline as false otherwise: the CLA should have requested this first else if (_stateManager.ControlSession.ReqControlState != ControlState.Offline) { _logger.Warn( "TLC set Application.ControlState to ControlState.Offline. (Requested = {0}).", _stateManager.ControlSession.ReqControlState); } // Otherwise Offline was requested else { _logger.Info("TLC set Application.ControlState to ControlState.Offline."); if (_stateManager.ControlSession.Type == ApplicationType.Control) { _logger.Info("Instruction to request control may now be send using RequestSessionStartControl()."); } } break; case ControlState.ReadyToControl: State.SessionControl = false; // Log if not as requested if (_stateManager.ControlSession.ReqControlState != ControlState.ReadyToControl) { _logger.Error("TLC set Application.ControlState to ControlState.ReadyToControl, while requested state is {0}.", _stateManager.ControlSession.ReqControlState); } // Otherwise log awaiting StartControl else { _logger.Info("TLC set Application.ControlState to ControlState.ReadyToControl. Now awaiting StartControl."); } break; case ControlState.InControl: // Log if not as requested if (_stateManager.ControlSession.ReqControlState != ControlState.InControl) { _logger.Error("TLC set Application.ControlState to ControlState.InControl. (Requested = {0}).", _stateManager.ControlSession.ReqControlState); } // Otherwise log confirmation else { _logger.Info("TLC set Application.ControlState to ControlState.InControl."); } break; case ControlState.StartControl: // If we requested control, take action to actually take it if (_stateManager.ControlSession.ReqControlState == ControlState.ReadyToControl) { _logger.Info("Application.ControlState set to ControlState.StartControl. (Requested = {0}).", _stateManager.ControlSession.ReqControlState); State.SessionControl = true; _gotIntersectionControlTime = DateTime.Now; Task.Run(() => SetReqControlStateAsync(ControlState.InControl), _sessionCancellationToken); } // Otherwise, log the error else { var startControlError = "Application.ControlState set to ControlState.StartControl, but application not ready."; _logger.Error(startControlError); throw new JsonRpcException((int)ProtocolErrorCode.Error, startControlError, null); } break; case ControlState.EndControl: // If the application is not in Control, log the error if (_stateManager.ControlSession.ReqControlState != ControlState.InControl && _stateManager.ControlSession.ReqControlState != ControlState.EndControl) { _logger.Error("TLC set Application.ControlState to ControlState.EndControl. (Requested = {0}).", _stateManager.ControlSession.ReqControlState); throw new JsonRpcException((int)ProtocolErrorCode.Error, "TLC set Application.ControlState to ControlState.EndControl, but application not in control.", null); } else { if (_stateManager.ControlSession.ReqControlState == ControlState.EndControl) { _logger.Info("TLC set Application.ControlState to ControlState.EndControl, which was requested."); } else { if (DateTime.Now.Subtract(_gotIntersectionControlTime) < TimeSpan.FromSeconds(180)) { _logger.Warn("TLC requested EndControl less than 180 after StartControl: {0} seconds.", DateTime.Now.Subtract(_gotIntersectionControlTime).TotalSeconds); } _logger.Info("TLC set Application.ControlState to ControlState.EndControl (outside request). " + "Confirming by setting requested state."); Task.Run(() => SetReqControlStateAsync(ControlState.EndControl), _sessionCancellationToken); } } break; default: var error = $"Application.ControlState cannot be set to {application.ControlState}: this state is undefined."; _logger.Error(error); throw new JsonRpcException((int)ProtocolErrorCode.Error, error, null); } _stateManager.ControlSession.ControlState = application.ControlState; if (_stateManager.ControlSession.ControlState.Value != ControlState.Error) { _logger.Debug("Application.ControlState set to " + _stateManager.ControlSession.ControlState); } } else { var error = $"UpdateState called with type Session, but {objectstateupdate.Objects.Ids.Length} instead of 1 object provided."; _logger.Error(error); throw new JsonRpcException((int)ProtocolErrorCode.Error, error, null); } break; case TLCObjectType.Intersection: for (var i = 0; i < objectstateupdate.Objects.Ids.Length; ++i) { var inter = _stateManager.InternalIntersections.FirstOrDefault(x => x.Id == objectstateupdate.Objects.Ids[i]); if (inter == null) { throw new JsonRpcException((int)ProtocolErrorCode.InvalidObjectReference, "Object " + objectstateupdate.Objects.Ids[i] + " unknown", null); } var sinter = (Intersection)objectstateupdate.States[i]; inter.StateTicks = sinter.StateTicks; if (sinter.State.HasValue) { inter.State = sinter.State; } if (sinter.ReqState.HasValue) { inter.ReqState = sinter.ReqState; } switch (sinter.State) { case IntersectionControlState.Error: case IntersectionControlState.Dark: case IntersectionControlState.Standby: case IntersectionControlState.AlternativeStandby: case IntersectionControlState.AllRed: case IntersectionControlState.SwitchOn: case IntersectionControlState.SwitchOff: State.IntersectionControl = false; break; case IntersectionControlState.Control: State.IntersectionControl = true; break; default: var error = $"Intersection.State cannot be set to {sinter.State}: This state is undefined."; _logger.Error(error); throw new JsonRpcException((int)ProtocolErrorCode.Error, error, null); } _logger.Debug("Intersection {0} state set to {1}", objectstateupdate.Objects.Ids[i], sinter.State); } break; case TLCObjectType.SignalGroup: case TLCObjectType.Detector: case TLCObjectType.Output: case TLCObjectType.Input: case TLCObjectType.Variable: for (var i = 0; i < objectstateupdate.Objects.Ids.Length; ++i) { var upob = _stateManager.FindObjectById(objectstateupdate.Objects.Ids[i], TLCFIClientStateManager.GetObjectTypeString(objectstateupdate.Objects.Type)); if (upob == null) { throw new JsonRpcException((int)ProtocolErrorCode.InvalidObjectReference, "Object " + objectstateupdate.Objects.Ids[i] + " unknown", null); } try { ((TLCObjectBase)upob).CopyState(objectstateupdate.States[i]); } catch (InvalidCastException e) { throw new JsonRpcException((int)ProtocolErrorCode.InvalidObjectReference, "Object " + objectstateupdate.Objects.Ids[i] + " seems not to be of type " + objectstateupdate.Objects.Type, e); } } break; case TLCObjectType.SpecialVehicleEventGenerator: if (objectstateupdate.Objects.Ids.Length == 1 && objectstateupdate.Objects.Ids[0] == _stateManager.ControlSession.Id && objectstateupdate.States.Length == 1) { _stateManager.SpvhGenerator.CopyState(objectstateupdate.States[0]); } else { var error = $"UpdateState called with type SpecialVehicleEventGenerator, but {objectstateupdate.Objects.Ids.Length} instead of 1 object provided."; _logger.Error(error); throw new JsonRpcException((int)ProtocolErrorCode.Error, error, null); } break; case TLCObjectType.TLCFacilities: var tlcferror = "UpdateState called with type TLCFacilities, which has no state."; _logger.Error(tlcferror); throw new JsonRpcException((int)ProtocolErrorCode.Error, tlcferror, null); default: throw new ArgumentOutOfRangeException(); } }