1 // 2 // Copyright (c) 2010-2024 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System; 9 using System.Collections.Generic; 10 using System.Linq; 11 using Antmicro.Renode.Core; 12 using Antmicro.Renode.Core.CAN; 13 using Antmicro.Renode.Exceptions; 14 using Antmicro.Renode.Logging; 15 using Antmicro.Renode.Peripherals; 16 using Antmicro.Renode.Peripherals.CAN; 17 using Antmicro.Renode.Time; 18 19 namespace Antmicro.Renode.Tools.Network 20 { 21 public static class CANHubExtensions 22 { CreateCANHub(this Emulation emulation, string name, bool loopback = false, bool useNetworkByteOrderForLogging = true)23 public static void CreateCANHub(this Emulation emulation, string name, bool loopback = false, bool useNetworkByteOrderForLogging = true) 24 { 25 emulation.ExternalsManager.AddExternal(new CANHub(loopback, useNetworkByteOrderForLogging), name); 26 } 27 } 28 29 public sealed class CANHub : IExternal, IHasOwnLife, IConnectable<ICAN>, INetworkLog<ICAN> 30 { CANHub(bool loopback = false, bool useNetworkByteOrderForLogging = true)31 public CANHub(bool loopback = false, bool useNetworkByteOrderForLogging = true) 32 { 33 sync = new object(); 34 attached = new List<ICAN>(); 35 handlers = new Dictionary<ICAN, Action<CANMessageFrame>>(); 36 this.loopback = loopback; 37 UseNetworkByteOrderForLogging = useNetworkByteOrderForLogging; 38 } 39 AttachTo(ICAN iface)40 public void AttachTo(ICAN iface) 41 { 42 lock(sync) 43 { 44 if(attached.Contains(iface)) 45 { 46 throw new RecoverableException("Cannot attach to the provided CAN periperal as it is already registered in this hub."); 47 } 48 attached.Add(iface); 49 handlers.Add(iface, message => Transmit(iface, message)); 50 iface.FrameSent += handlers[iface]; 51 } 52 } 53 DetachFrom(ICAN iface)54 public void DetachFrom(ICAN iface) 55 { 56 lock(sync) 57 { 58 attached.Remove(iface); 59 iface.FrameSent -= handlers[iface]; 60 handlers.Remove(iface); 61 } 62 } 63 64 Start()65 public void Start() 66 { 67 Resume(); 68 } 69 Pause()70 public void Pause() 71 { 72 lock(sync) 73 { 74 started = false; 75 } 76 } 77 Resume()78 public void Resume() 79 { 80 lock(sync) 81 { 82 started = true; 83 } 84 } 85 86 public bool IsPaused => !started; 87 88 public event Action<IExternal, ICAN, ICAN, byte[]> FrameTransmitted; 89 public event Action<IExternal, ICAN, byte[]> FrameProcessed; 90 public event Action<IExternal, ICAN, CANMessageFrame> FrameReceived; 91 92 public bool UseNetworkByteOrderForLogging { get; set; } 93 Transmit(ICAN sender, CANMessageFrame message)94 private void Transmit(ICAN sender, CANMessageFrame message) 95 { 96 lock(sync) 97 { 98 this.Log(LogLevel.Debug, "Received from {0}: {1}", sender.GetName(), message); 99 FrameReceived?.Invoke(this, sender, message); 100 101 byte[] frame = null; 102 try 103 { 104 frame = message.ToSocketCAN(UseNetworkByteOrderForLogging); 105 } 106 catch(RecoverableException e) 107 { 108 this.Log(LogLevel.Warning, "Failed to create SocketCAN from {0}: {1}", message, e.Message); 109 } 110 if(frame != null) 111 { 112 FrameProcessed?.Invoke(this, sender, frame); 113 } 114 115 if(!started) 116 { 117 return; 118 } 119 if(!TimeDomainsManager.Instance.TryGetVirtualTimeStamp(out var vts)) 120 { 121 vts = new TimeStamp(default(TimeInterval), EmulationManager.ExternalWorld); 122 } 123 foreach(var iface in attached.Where(x => (x != sender || loopback))) 124 { 125 iface.GetMachine().HandleTimeDomainEvent(iface.OnFrameReceived, message, vts, 126 frame != null ? () => FrameTransmitted?.Invoke(this, sender, iface, frame) : (Action)null); 127 } 128 } 129 } 130 131 private readonly List<ICAN> attached; 132 private readonly Dictionary<ICAN, Action<CANMessageFrame>> handlers; 133 private bool started; 134 private readonly object sync; 135 private readonly bool loopback; 136 } 137 } 138 139