WEBVTT 00:00.000 --> 00:11.760 Okay, I'm Martin Taichmann. I'm coming from the European 00:11.760 --> 00:18.720 expert. Yeah. We are running the world's largest x-ray laser. Why does it think 00:18.720 --> 00:25.800 go so fast? Yeah, I just use the other keys. So we are running the world's largest x-ray 00:25.800 --> 00:30.680 laser. So most of us is in tunnels. So it's three and a half kilometers long that's quite 00:30.680 --> 00:36.840 some big size. We are publicly refunded research facility in Hamburg, Germany. And as you all 00:36.840 --> 00:42.360 can kind of imagine in a large x-ray facility like that, we certainly need an electron 00:42.360 --> 00:52.040 bio-fingent polarization focus, EVPS. I've put up a picture of that. So it's actually 00:52.040 --> 00:56.440 down here. You can actually see it, but well, I just put a picture anyway. What you're seeing 00:56.440 --> 01:02.600 here is just some bunch of electronics. And that bunch of electronics controlling those 01:02.600 --> 01:10.920 motors, which you see up here, which is a motion. And this electronics is using the EVP either 01:10.920 --> 01:18.040 cut protocol, which is a very versatile bus system that they use throughout every area throughout 01:18.120 --> 01:23.720 the facility. We have a huge number of installations from that. It's effectively enough 01:24.360 --> 01:30.840 hardware-wise to just control everything we need. Unfortunately, the software is just awful 01:30.840 --> 01:37.320 and it's close source. So we try to replace that with the nice open source software. 01:38.520 --> 01:44.760 Okay. How does this protocol work? Well, in that protocol, you just have a just a normal computer 01:44.840 --> 01:50.360 that sends out either net packets, with two of those terminals, which you just saw on the picture 01:50.360 --> 01:56.040 before. And that easy change shows the packet to the first one, which is set to the second one, 01:56.040 --> 02:02.040 and so on. And the last one just sends it back to the computer. And so reading from the 02:02.040 --> 02:06.760 terminals, by the way, you do by those packets being modified on the fly by those terminals, 02:06.760 --> 02:13.240 and you're just other stuff coming back than you sent. And now you might immediately see the 02:13.320 --> 02:20.600 application for EVP F, because what I'm not doing is, well, EVP F is running in the 02:20.600 --> 02:26.360 in the lowest corner, and it's running pretty fast, and it has direct access to the network via 02:27.800 --> 02:33.720 XTP programs, with the program type XTP. And so what we are just doing, we just are just within 02:33.720 --> 02:38.280 this loop. So the packet we send out a packet, it goes through all the terminals, comes back, goes through 02:38.440 --> 02:45.720 our EVP F, and send out again, and so on just in a circle, there's a nice loop. And on a high 02:45.720 --> 02:50.600 level, all the flows, so this is the fast stuff, and all the slow stuff we can just do in the 02:50.600 --> 03:00.200 user space, from Python. So what I want to talk about this in this talk, just after this fast introduction. 03:01.320 --> 03:07.960 So I want to show how you can use EVP F cap to make XTP programs with some simple examples. 03:08.680 --> 03:15.000 XTP, as a reminder, is just the program type and EVP F for network transport. Then I show 03:15.000 --> 03:21.560 it actually works. Then it goes through some EVP features that are already supported. And as promised, 03:21.560 --> 03:27.800 it shows some cool motion stuff from as an application for EVP F. But we are all constraints 03:27.800 --> 03:36.600 from developing this software, well, we need to be highly dynamic. And so we only get the hardware 03:36.600 --> 03:42.120 set up at runtime. We read out the terminals, we read out what's going on, how they are set up 03:42.120 --> 03:48.680 at runtime. So you have no clue about how to send out data before we have actually seen it. 03:49.240 --> 03:56.680 Even the actual logic inside might be not clear at compile time, so we just, it's just discovered 03:56.680 --> 04:01.960 always at runtime. We also have, as I said, many installations of those, and we would like to be 04:02.040 --> 04:10.680 able to just run several installations on one computer. And so we wanted several IPBF programs programs 04:10.680 --> 04:16.520 to run in parallel. And those programs must be replaceable at runtime, because we want to be able 04:16.520 --> 04:21.800 to replace hardware at runtime and then just change the accordingly. So simplicity is another 04:21.800 --> 04:26.440 important thing. Most of the programs, most of our programming is done by scientists, 04:26.520 --> 04:32.760 and not by programmers, and those scientists. There are no Python. Python is huge in our community, 04:33.480 --> 04:38.440 but they might not be the best programmers out there, and that's not really their jobs. 04:38.440 --> 04:44.040 I should be simply enough that they can deal with it. Also flexibility, I did not want to write 04:44.040 --> 04:49.160 a thing which just solves my problem. I just wanted it to be usable for many other purposes. 04:49.800 --> 04:55.720 My ideas that EVP F can be used as a speed boost in Python programs, so they can just offload 04:56.680 --> 05:04.680 the inner loop of your Python program into the Linux channel. Okay, let's just jump directly 05:04.680 --> 05:14.680 in and give a full example. Here we have one. This is an entire code. Well, no. It doesn't 05:14.680 --> 05:20.520 like me here. This is an entire program. This is not just a code snippet. This is really the 05:20.520 --> 05:26.280 entirety of the program. You can just like that Python and it will do it. It's a very 05:26.280 --> 05:32.440 simple example of just accounts, the number of packages that are coming in. It looks like no, 05:32.440 --> 05:40.200 a Python code. You might not even notice anything special, but this is actually EVP F from. 05:40.200 --> 05:44.840 How do you see? So let me just go through it. The EVP F part is this part here. 05:45.800 --> 05:50.040 You only see from the fact that it inherits from XDP, so then XDP program. 05:51.160 --> 05:56.600 We like always the Linux kind of after for which license we use, we are at first them. So 05:56.600 --> 06:04.520 obviously that's GPL. We can declare a clear and airy map for a communication with user space 06:04.520 --> 06:12.040 and then also declare a variable that lives in that airy map. This here is now my EVP F program. 06:13.000 --> 06:17.080 Then comes the user space part. And what we do here, we just 06:18.520 --> 06:26.760 insensiate this class here. Say run. Then it gets attached to this network port. And while it 06:26.760 --> 06:34.840 while the EVP F is running in the kernel, we can just talk to it. And now the interesting parts of it. 06:35.080 --> 06:43.160 So this part here generates EVP F. And I will repeat that probably 10 times during this talk. 06:43.160 --> 06:49.000 This is not the code that is actually executed in the kernel, but this code is only executed 06:49.000 --> 06:53.880 once at the very beginning. And generates code that then is injected into the external, 06:53.880 --> 06:58.520 and runs then in the external, the result of that. That's the first thing. 06:59.640 --> 07:04.360 The second thing is you might notice that this variable here, and this variable there, 07:04.360 --> 07:09.800 is the same. And they look the same. It's the EVP F care that in the background 07:09.800 --> 07:15.560 to make them the same. And so you can access the same variables from EVP F and from user space 07:15.560 --> 07:27.800 without even feeling it. Yes. XDP doesn't really make sense if you want to see the packet. 07:27.800 --> 07:33.640 Do you want to see the data that is coming in? So for that as a first thing, we need to declare 07:33.720 --> 07:38.200 a minimum packet size so that the verify analysis, otherwise we just drop the packets or do 07:38.200 --> 07:44.680 something else. Does matter here. Then we can declare a packet variable, so a variable that 07:44.680 --> 07:51.560 lives in the space of the packet. And if you know either an protocol by heart, you know that 07:51.560 --> 07:57.800 a position 12, you have the either type. And the either type is actually unsigned to 16 bit 07:57.800 --> 08:05.560 in a network byte order. And we are following the Python's struct package convention, 08:05.560 --> 08:11.240 very just H, which is unsigned to 16 bit, and the summation mark means a network byte order, 08:11.240 --> 08:16.840 just Python programmers. They all know that don't they. And so you keep our user spaces 08:16.840 --> 08:21.880 stuff the same. We have our program. And now we can just filter for either type equals is 0, 08:22.760 --> 08:29.960 that is by the way IPv4. And as I said, already this code is executed only once, 08:30.680 --> 08:36.200 and you better execute all the code. So this is why we cannot have if statements. Because if 08:36.200 --> 08:40.760 you have an if statement, you do not execute all the execute all the code. So what you do we just 08:40.760 --> 08:48.440 abuse the Python with statement, which in the beginning looks a bit awkward. You get used to it. 08:48.440 --> 08:54.440 So now it's the next thing you might want to have more than more than one test for example 08:54.440 --> 08:59.440 we could check how many want to know how many FPV6 pay packets are passing through. 08:59.440 --> 09:01.440 How do you do that? 09:01.440 --> 09:02.440 That is how you do it. 09:02.440 --> 09:05.440 We just make another user space variable. 09:05.440 --> 09:09.440 And test is nice with condition as else. 09:09.440 --> 09:15.440 And with else, comma is nothing else than what you normally read as illiff. 09:15.440 --> 09:19.440 And yes, it looks a bit unused. 09:19.440 --> 09:26.440 It's strange, but it's not too far of a normal programming. 09:26.440 --> 09:28.440 So how does it actually work? 09:28.440 --> 09:33.440 Well, as you might have noticed, those declarations appear. 09:33.440 --> 09:35.440 They are Python descriptors. 09:35.440 --> 09:38.440 And in Python descriptors, you can have, 09:38.440 --> 09:40.440 don't get and don't set methods. 09:40.440 --> 09:44.440 And those don't get and set methods can do whatever you like. 09:44.440 --> 09:48.440 Usually, they set the value of your variables somehow. 09:48.440 --> 09:52.440 But you can also just make them generate ebps code. 09:52.440 --> 09:53.440 And so it happens. 09:53.440 --> 09:56.440 So it just sees there's just self that speed. 09:56.440 --> 09:57.440 Speed knows it's okay. 09:57.440 --> 09:59.440 It's a packet variable in the background. 09:59.440 --> 10:03.440 ebps can't have set up that the packet. 10:03.440 --> 10:05.440 Start with the packet point. 10:05.440 --> 10:07.440 I will be in register eight here. 10:07.440 --> 10:11.440 We know that in offset of 12, we just put the 12th in. 10:11.440 --> 10:16.440 At the same time, we have our area map, which if you path in the background, 10:16.440 --> 10:20.440 put it's a base pointer to register nine. 10:20.440 --> 10:26.440 Set up some, laid out some, the user space variables so that they are at position 16. 10:26.440 --> 10:28.440 You put that into registers. 10:28.440 --> 10:36.440 And then the rest statement will just generate a jump that jumps over that code. 10:36.440 --> 10:40.440 If the condition is not met. 10:40.440 --> 10:42.440 Okay. 10:42.440 --> 10:48.440 So the advantage of all of that is that I don't have any dependencies. 10:48.440 --> 10:50.440 So technically, it's written in Python. 10:50.440 --> 10:52.440 So yes, Python is a dependency. 10:52.440 --> 10:55.440 And yes, Linux is a dependency. 10:55.440 --> 10:59.440 No, I will not plot that to Windows just in case you're wondering. 10:59.440 --> 11:02.440 And yes, lib c is actually a dependency. 11:02.440 --> 11:06.440 So I think you will have a hard time running that in Alpine Linux. 11:06.440 --> 11:07.440 But okay. 11:07.440 --> 11:10.440 So it's just to pip install the ebps can't. 11:10.440 --> 11:11.440 And it's there. 11:11.440 --> 11:15.440 It doesn't even do not in dependencies because it has no dependencies. 11:15.440 --> 11:17.440 And I think that's very useful, especially for beginners. 11:17.440 --> 11:20.440 You can just start working. 11:20.440 --> 11:25.440 Some features that we already have. 11:25.440 --> 11:26.440 Why does that jump go? 11:26.440 --> 11:28.440 Is this a no? 11:28.440 --> 11:30.440 What I call permanent local programs. 11:30.440 --> 11:33.440 So what I showed you until now. 11:33.440 --> 11:38.440 Always the program was running while the Python program was running. 11:38.440 --> 11:40.440 So this is this example here. 11:40.440 --> 11:42.440 If you program, we insensiate it. 11:42.440 --> 11:43.440 You say program that run. 11:43.440 --> 11:45.440 It's within a viz block. 11:45.440 --> 11:49.440 And within the viz block, your ebps code is running. 11:49.440 --> 11:52.440 And you can do whatever a business logic you want. 11:52.440 --> 11:57.440 But you also want my might want to have that the program is running all the time. 11:57.440 --> 11:59.440 Even after your Python program has finished. 11:59.440 --> 12:01.440 Well, you can also do that. 12:01.440 --> 12:03.440 You just insens instantiate your Python program. 12:03.440 --> 12:06.440 Then you just attach this to the ebps port. 12:06.440 --> 12:12.440 And what you can then do is you can pin all the maps to the bps file system. 12:12.440 --> 12:15.440 Then you can just do whatever business logic you want to do. 12:15.440 --> 12:17.440 Your program finishes. 12:17.440 --> 12:19.440 The bbps continuous to run. 12:19.440 --> 12:22.440 It still filters all the packets and whatnot. 12:22.440 --> 12:26.440 And in a later time, you can just insert in a new, 12:26.440 --> 12:28.440 newly started Python program. 12:28.440 --> 12:32.440 It can just insensiate that thing again with this nice parameter load maps. 12:32.440 --> 12:34.440 It will just load all the bps maps back. 12:34.440 --> 12:40.440 So this means you can still access all the user space variables from again. 12:40.440 --> 12:43.440 And you do whatever business logic that you want to do. 12:43.440 --> 12:46.440 And in the end, you just detach it and you're done. 12:46.440 --> 12:47.440 OK. 12:47.440 --> 12:51.440 So I promise you to do a show you how to do motion control with the app, 12:51.440 --> 12:53.440 which is something really weird. 12:53.440 --> 12:56.440 So how would you do that first? 12:56.440 --> 12:59.440 The first thing is, what do you prepare to cut does for you? 12:59.440 --> 13:01.440 And you don't need to do that yourself. 13:01.440 --> 13:04.440 It reads all this app's self descriptions of the terminals, 13:04.440 --> 13:08.440 which are on your bus. 13:08.440 --> 13:13.440 And then you create an evened package that contains from that self description. 13:13.440 --> 13:18.440 It knows how the terminals want to be talked to. 13:18.440 --> 13:23.440 Then you generate an evened packet that contains all the interesting information 13:23.440 --> 13:24.440 that sure is in header. 13:24.440 --> 13:27.440 And then in the package, somewhere there's whatever in the encoder positions, 13:27.440 --> 13:31.440 like you're in readout, some motor speed that you want to move your motor build, 13:31.440 --> 13:36.440 some limits which are said, whatever you might be in your hardware that there is. 13:36.440 --> 13:38.440 Can be a lot actually. 13:38.440 --> 13:46.440 Then you have to program all the terminals so that they know how this packet is supposed to look like. 13:46.440 --> 13:52.440 And only then we can generate the EBPF program the same way as I have just shown in 13:52.440 --> 13:56.440 the last example. 13:56.440 --> 14:00.440 Then we just send one of those prepared packages on the network. 14:00.440 --> 14:03.440 It goes through all the terminals, comes back. 14:03.440 --> 14:09.440 Then we process by XDP, get sent out again and all over again in a big circle. 14:09.440 --> 14:12.440 And this is how you do your motion control. 14:12.440 --> 14:17.440 The while there's a slow code at the same time to communicate with the outside world, 14:17.440 --> 14:23.440 they'll just run as is in user space. 14:23.440 --> 14:33.440 One other feature that I need for that is to tell you that we want to be able to address different kinds of hardware at the same time. 14:33.440 --> 14:36.440 Also independently from each other. 14:36.440 --> 14:44.440 The way I do that is that I start some permanent program that has a Pint Prog area map. 14:44.440 --> 14:56.440 And then into the header, I just secretly sneak in some breadcrum, some number from which I know which part of the hardware this packet is supposed to talk to. 14:56.440 --> 15:02.440 And then what I just do is I call tail call into that program area. 15:02.440 --> 15:04.440 So I just know which number it is. 15:04.440 --> 15:08.440 Okay, and then I know which program area to tail call. 15:08.440 --> 15:16.440 And then any program that wants to actually communicate with one part of the hardware can just start. 15:16.440 --> 15:24.440 Well, instead it's even if program into that program area will be called for all the data that it needs. 15:24.440 --> 15:37.440 And once it's done, it just removes itself from the program area and everything continuous at continuous as before. 15:37.440 --> 15:40.440 So Python gives you the large advantage. 15:40.440 --> 15:52.440 This is that you can just go to a much higher level of much higher level of programming of understanding. 15:52.440 --> 16:06.440 And so what you can do in ebf is ebf cat in my library is that you can abstract out all those details about packets or whatever. 16:06.440 --> 16:16.440 And have this kind of programs where it's really focused on this control part of ebf cat. 16:16.440 --> 16:24.440 For example, you have in in a motor device you want to a motor usually has something like a velocity it usually has a position from an encoder. 16:24.440 --> 16:27.440 That is something that you communicate with the hardware. 16:27.440 --> 16:35.440 So and ebf cat sees not just one of those but has probably many of those for all the coaches that you have in your system. 16:35.440 --> 16:43.440 And it will just lay out the packet as it will and then just assigns the where it will be. 16:43.440 --> 16:49.440 Also then you're talking to talking to the user which I hear then call a device law. 16:49.440 --> 16:58.440 It will just then also just take all the motors the communication with the user lay out the map to communicate with user space. 16:58.440 --> 17:04.440 And so that in the ebf program. 17:04.440 --> 17:06.440 Doesn't see anything of that anymore. 17:06.440 --> 17:18.440 And it just for you it's just velocity and entire position if you ever did motion control you can see that this is just kind of the easiest way to do a position control of a motor. 17:18.440 --> 17:30.440 And the nice thing is that you don't you don't really see that this is actual you know ebf code that does a lot of things just going on in the background. 17:30.440 --> 17:42.440 You can do this very high level version of programming and I hope that then even my colleagues will be able to do that. 17:42.440 --> 17:45.440 Okay, I'm already at my summary. 17:45.440 --> 17:50.440 So with ebf cat you can generate ebf code from Python. 17:50.440 --> 18:02.440 It can be used generically to use ebf programs especially exp programs and as a special case it can be used for computer controls of either compatible hardware. 18:02.440 --> 18:15.440 Everything can't be found and get up the documentation here and for those who are super interested to hear are all my motion control things are in this additional really repositories. 18:15.440 --> 18:20.440 I'm thanking my colleagues and to you for your attention. 18:20.440 --> 18:28.440 But just before you get to come up with your questions I want to then show you the result on how it looks like. 18:28.440 --> 18:32.440 Okay, now I would need to know how to get to there it is. 18:33.440 --> 18:37.440 How is such a motion that looks like and. 18:37.440 --> 18:49.440 There is this is a ebbbf moving in sync as you see you two different motors three different motors actually to keep them all in sync. 18:49.440 --> 18:55.440 Questions. 18:56.440 --> 19:01.440 Thanks so very interesting talk so if I just and correctly you are generating. 19:01.440 --> 19:07.440 Bbf instructions directly out of the Python code right you're not using any like L of a more anything. 19:07.440 --> 19:08.440 Exactly. 19:08.440 --> 19:15.440 I can imagine must be a hard task so are there any limitations like I don't know you run out of registers. 19:15.440 --> 19:20.440 Can you store stuff on the stack or you know that must be like it's a hard task. 19:20.440 --> 19:24.440 Absolutely this is a hard task but absolutely I'm not done yet. 19:24.440 --> 19:30.440 I'm kind of always writing as I go and I thought actually that this would be a much bigger problem than it is. 19:30.440 --> 19:32.440 Weirdly so. 19:32.440 --> 19:34.440 I have so. 19:34.440 --> 19:38.440 The entirety of that code is less than 10,000 lines. 19:38.440 --> 19:46.440 So it's not a huge code and two thirds of that code by the way is that either could protocol which is not simple. 19:46.440 --> 19:50.440 So it is until now pretty simple and yes I know I until now it's right. 19:50.440 --> 20:00.440 I have not yet written a proper register allocator and do that very greatly and yes if you need too much register it will just crash until now. 20:00.440 --> 20:06.440 But interestingly I have have actually I've had to have written a rather complicated code. 20:06.440 --> 20:14.440 Already and until now I have not yet been into the limitation and will run into that I will figure out how to fix it. 20:14.440 --> 20:16.440 That's cool. 20:24.440 --> 20:26.440 I think we'll talk. 20:26.440 --> 20:40.440 I'm not that familiar with neither is a cart or BTF but I guess you do so the question is could it be used to implement hard real time communication with is a cart. 20:40.440 --> 20:42.440 Maybe. 20:42.440 --> 20:52.440 If so there is other people doing talks about using EPPF for controlling the scheduler for example. 20:52.440 --> 21:02.440 And there's even I've read somewhere somebody apparently is even working about working using EPPF for the scheduler of the packets going out of the hardware. 21:02.440 --> 21:08.440 And if I have that then then I can do hard real time. 21:08.440 --> 21:14.440 So please just work scheduler for packets outgoing packets please. 21:14.440 --> 21:18.440 Would be really nice for me then I can do a real hard time. 21:18.440 --> 21:22.440 Until now what I'm doing is just I'm just so fast with EPPF. 21:22.440 --> 21:30.440 Then just fast then everything that the normal real time people do is just to blast them just away. 21:30.440 --> 21:34.440 Okay thank you. 21:34.440 --> 21:38.440 Any other questions? 21:38.440 --> 21:52.440 So you mentioned not having any dependencies does it also mean that you do all of the BFF's schools manually instead of using that that's actually pretty challenging as well. 21:52.440 --> 21:56.440 I know I don't experience so. 21:56.440 --> 22:02.440 As I said it's in total less than 10,000 land of code it's the very fires alone is bigger. 22:02.440 --> 22:06.440 And Python is just very very short. 22:06.440 --> 22:08.440 Really impressive. 22:08.440 --> 22:10.440 Cool thing. 22:16.440 --> 22:18.440 Can you talk a bit more? 22:18.440 --> 22:26.440 You've been choosing XTP specifically because of the I guess low latency or are there also lots of packets that are going to. 22:26.440 --> 22:35.440 It's it's really the latency I actually even have I even have the problem that many hardware is optimized for high bandwidth which is usually not good for low latency. 22:35.440 --> 22:46.440 This is why I'm running XTP on a Raspberry Pi because Raspberry Pi's do not have the necessary buffers to do for high bandwidth. 22:46.440 --> 22:52.440 But this way have a pretty low latency. 22:52.440 --> 22:58.440 Did you also use this in combination with busy polling or something? 22:58.440 --> 23:00.440 The busy polling of what. 23:00.440 --> 23:04.440 So kind of what I'm kind of doing is constantly polling yes. 23:04.440 --> 23:12.440 So that the the the packets constantly flowing so usually usually at least 10 kilohertz the packets going in out in out constantly. 23:12.440 --> 23:16.440 So it sucks this cycle goes really the 10 kilohertz if not 20 kilohertz. 23:16.440 --> 23:20.440 That is effectively that's busy polling. 23:20.440 --> 23:24.440 I think that's the thing that's what it is. 23:24.440 --> 23:34.440 I don't think you've been running that in production and what what feedback you get from the other uses of the project like did they stumble on some. 23:34.440 --> 23:39.440 Maybe if stuff somehow like how is the experience. 23:39.440 --> 23:41.440 So I've been working on that since five years. 23:41.440 --> 23:45.440 It started very slowly but it's now taking speed. 23:45.440 --> 23:55.440 My colleagues at the when I switched over from the closed source stuff that used to before to this. 23:55.440 --> 24:01.440 They didn't even notice which I think is a it I think it's a good thing. 24:01.440 --> 24:07.440 So it was just working so I asked them did you notice any problems anything. 24:07.440 --> 24:09.440 They thought I'll find. 24:09.440 --> 24:17.440 I have presented it to scientific conferences to scientists and they were just they were really happy to. 24:17.440 --> 24:21.440 They're the only question that the the meanest question that I got. 24:21.440 --> 24:27.440 I didn't you're just right all of that in C++ and. 24:27.440 --> 24:33.440 So they also loved it so until now I got a lot of positive feedback. 24:33.440 --> 24:35.440 And it's just because it's just working. 24:35.440 --> 24:41.440 Have you heard from the lawyers from the company sitting the proprietary stuff yet. 24:41.440 --> 24:43.440 Until now no. 24:43.440 --> 24:51.440 So the company insists that they have a pattern and all of that but they are very fruit they say that if they only. 24:51.440 --> 24:55.440 They're only interested in the hardware side. 24:55.440 --> 24:59.440 So that's it's an open protocol so everybody can. 24:59.440 --> 25:05.440 Even make hardware like that but I'm softer and they always they say that for softer they have no issues because. 25:05.440 --> 25:09.440 They want to sell the hardware and that you send the sell the software makes software for it. 25:09.440 --> 25:11.440 They're officially state that. 25:11.440 --> 25:12.440 Go for it. 25:12.440 --> 25:13.440 Very good. 25:13.440 --> 25:14.440 Thank you. 25:14.440 --> 25:15.440 Any other question. 25:15.440 --> 25:16.440 Yeah. 25:16.440 --> 25:18.440 Last question. 25:18.440 --> 25:19.440 Yeah. 25:19.440 --> 25:20.440 Thank you. 25:20.440 --> 25:24.440 How is it now if you want to give that to other scientists. 25:24.440 --> 25:28.440 I assume you you probably will need some UI of some form like. 25:28.440 --> 25:32.440 So you have your device view and your health checks and all that stuff that. 25:32.440 --> 25:34.440 Twink and then the others do. 25:34.440 --> 25:36.440 So this certainly this. 25:36.440 --> 25:42.440 The second link on that page is the communication to the communication. 25:42.440 --> 25:46.440 Layer that bridges it to our in-house control system called carbo. 25:46.440 --> 25:52.440 Which then so it gives you all kinds of UIs and whatever you want to have. 25:52.440 --> 25:54.440 It's by the way all open source. 25:54.440 --> 25:55.440 Look at it. 25:55.440 --> 25:57.440 It's actually pretty nice. 25:57.440 --> 26:00.440 Maybe somebody wants to work on that. 26:00.440 --> 26:02.440 Just. 26:02.440 --> 26:03.440 All right. 26:03.440 --> 26:04.440 Thank you. 26:04.440 --> 26:05.440 Thank you.