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