Migration of a Monolithic Algorithm to Service-Oriented Architecture (SOA) - MATLAB
Video Player is loading.
Current Time 0:00
Duration 24:36
Loaded: 1.64%
Stream Type LIVE
Remaining Time 24:36
 
1x
  • Chapters
  • descriptions off, selected
  • captions off, selected
      Video length is 24:36

      Migration of a Monolithic Algorithm to Service-Oriented Architecture (SOA)

      Mark Danielsen, MathWorks

      Service-oriented architectures have been around for a while. In this presentation, see how to break apart a monolithic algorithm into services that can be reused. Follow an explanation of guiding principles and examples of breaking apart a large algorithm into service-based components using System Composer™ as a method to be refactored, and then see how to reuse the test from the large component to the newly refactored composition of SOA components. Finally, learn to generate C++ code (via Adaptive AUTOSAR®) across the new composition of SOA-based components.

      Published: 15 May 2024

      So hi. I'm Mark Danielson. And I'm an application engineer in our Novi office, and I work in more of a production code generation, System Composer and AUTOSAR content. And today, we're going to talk about the migration of a monolithic algorithm to a service-oriented architecture.

      Now, service-oriented architectures are not new. We're going to go through a very simple example that will kind of frame up a good part of our discussion for today. So let's talk about the weather. Your phone is not actually smart enough to be able to characterize or forecast the weather.

      And that's why we have a weather app on our phone. This weather app calls out to a weather service that's in the cloud. The weather service is going to get a request, and it's going to respond with a block of data that talks about the forecast of the temperatures and the weather. And your phone is going to get that block of data back. And it's actually going to decode and display the information.

      Hey, today it's sunny in Michigan. Some more details about this, though. You have a weather application. You're calling out to a weather service that's in the cloud. And you might be calling out to that weather service by means of a message, a function API, some type of command or function prototype.

      The weather service is providing some function or service. In this case, it's providing a block of data that tells you about what the weather forecast is. And the service may or may not respond, depending on the function that it's providing, it may or may not respond back to the actual weather application itself or the application that's calling it.

      One more interesting point-- because the weather service is in the cloud, it's actually accessible to millions of users across the country. So there's reusability and accessibility. And it's not just bashing out messages of what the weather service is. It's really about the weather information that it's sending you per your location. So it is providing you with a unique service.

      Now, that simple example really does kind of frame up a good portion of our conversation. So we'll kind of frontload with some key takeaways. We have the tools to help you move to service-oriented architectures. We can generate C++, very good C++ code from our service-oriented based components.

      And we're going to show you a lot of information in the next couple of minutes. We don't expect you to be subject matter experts after this talk. So that we hope that you will engage with us in your service-oriented-architecture-lilke projects. So definitely, we are here to help.

      So the challenge problem is, how do I partition my algorithm into smaller pieces-- more reusability, portability, and still maintain functionality of the original algorithm? And that requires a lot of engineering choices, engineering designs, engineering rigor. And we're not actually going to tell you about that part.

      However, we'll show, once you've made those partitioning decisions, how to create a service-oriented architectural framework, how to create an interface to service components, how to create the Simulink behavior model. And we will show, in maybe the longer version of this talk, how to create agnostic C++ code and apply adaptive AUTOSAR and generate C++ code. Today, we're just going to touch on how-- we're going to just touch on that we can generate code, show you the code. But for a longer talk, we definitely can really show you how to generate the C++ code for agnostic code or the adaptive AUTOSAR platform.

      Now, I'm building on two different projects that came in the past, full disclosure. The project on the left of the screen actually is from my colleagues, Nukul and Shwetha. And they worked on presenting how to migrate these components to service-oriented architectural frameworks. They presented this to the AUTOSAR Open Conference in the spring, and there's a technical article here that we'll give you the link to.

      The second project that we're building on came from my colleagues, Sameer and Nitish. And they worked on more of an application for software-defined vehicle. And they had a bunch of nice vehicle-based models that are probably more in line with what you have today. Now, we're not going to expand on the software-defined vehicle. What we're going to do is steal their models to use in our project to apply the principles of the first presentation to.

      Now, there's a trend. Automakers are embracing software-oriented architectures and standards. And we're coming from the days where we had more of a powertrain control module or body control module when we applied model-based design. And nowadays, people are moving to towards software-defined vehicles, autonomous connectivity, AI, and they're moving to software-oriented platforms like adaptive AUTOSAR, ROS, and DDS.

      But the real question is, why is service-oriented architectures so attractive to automakers? So by creating service components, we're really creating reusable services that may be shared across many applications and many components. We have a potential for relocation, like we looked at the weather service in the cloud where the actual service was located in the cloud. Could have that relocation into basic software for that functionality, or could even be relocated into other components.

      We also have a higher level of testability. So when we're pulling parts of our function away from our original model and we're putting them into service component, they're a smaller piece, smaller building block, we have a higher potential for testability in terms of, I can now exhaustively test that piece. I have a higher granularity of testing, and I can prove that building block and have a higher confidence level, which also might lead for a potential for reduction in validation due to sharing of functions across applications or other components.

      Now, we're going to talk about the process of decomposition to composition. And there are really four nice steps here we have, identify and analyze services, define services for our interfaces, and then there's define service contracts and deploy our services. And the last two steps are really more towards the code generation and deploying to your target. We're actually going to spend more time on the first two steps.

      So this is really that process of pulling your legacy model apart and building in service components with the service interfaces and moving to a service-oriented architectural framework. Now, there are some guiding principles. But again, we're not going to talk about the engineering choices or decisions that you have to make. But we did want to tell you, there are some guiding principles. Some of these are debatable.

      You might, actually come up, as a company, you might come up with your own guiding principles. We'll show you the engineering choices that we made and how we're going to repartition our model. But it really is up to you to create your own principles and how you're going to pull these models apart.

      So the second presentation, again, is where I'm going to steal my models from. There are two controllers. One is a BMS controller, which has more of a battery-modeling management system on it. The other one is an HPC-type or High Performance Computing-type controller. And that's the one that has more of a vehicle controls algorithm on it.

      In this case, there is a Sport+ mode where, if we put the mode into a Sport mode, we're actually going to use the battery more aggressively. And this is going to translate to the other control module in terms of battery usage. If we're not in a Sport+ mode, then we're going to be more range friendly and have more of an eco-type mode. Now, it's not so important what the algorithm is. In a moment, you're going to see how we really look at the model in itself.

      So where do we start? So we start with a model that actually passes regression tests. And it's really important to have regression tests to be able to prove out your current functionality. And we want to reuse the test harness and test utilities that you have developed for this original model after we refactor the model into a composition of components.

      So the decisions that we made is that we truly do want to end up with a model that looks something like the current model. However, we want to replace certain blocks in here with function callers that will call out to service components. But we really do want to have our inputs and our outputs to stay the same because we're going to be driving that with our test harness and looking to make sure we have the same outputted results.

      But in the end, our end model will look like what's in the gray box. And again, we're going to refactor some services. So what is it that we're going to look at originally? So it turns out, all the models, all the blue boxes that we have on the chart, whether they're on the left side of the chart or the right side of the diagram, these all are small functions that represent some type of signal conditioning or creating additional signals. And the block in the middle, with the green, that's more of a main computational algorithm part.

      And so I'm going to refer to the blue boxes on the right-- or I'm sorry, my other right, the left-- as more of a pre-algorithm function. And then, the one box on the right is a post, and the stuff in the middle is more of our main algorithm or main computation. And then we could refactor everything into services.

      But at this point in time, we're going to just take a staged approach. And this lines up with more of a test early, test often, where we really want to make sure that, with a few amount of changes, we really have not lost any of our functionality. And we're going to do this with System Composer.

      Now, for some of you that may not be aware of System Composer, we have a brand new System Composer Onramp Training. And Onramps, anytime that you see, on our public site, an Onramp, it's really a free, self-paced training class. This one takes about an hour to get through and would bring you up to speed on what System Composer is and how it can help you.

      OK, now I drew an architectural diagram. And I'm going to go through an awful lot of steps in a very short amount of time to tell you how I created this diagram. So the first thing I did is I created an empty software architectural model. Then I drew the application component box. And then I drew all the inputs and outputs and connected them to the interface layer.

      Again, we want to drive all our test harness into this compositional model and get the outputs that represent the things that we're going to check with our test harness. I also then drew in all of the software component boxes for the new service components and connected them through client service type ports. And we'll talk about that more in just a second.

      But at this point in time, I have an architectural model that I've drawn, and the next step is to create client server interfaces. So now, I've highlighted to the right of my picture, I've highlighted what that zoomed-in part is. And I have a ability to put on my main application a client port. On the service component, I have the ability to have a service port.

      I have a service connector that connects the client to the service. I have a service connector name. And the purple highlight, what it represents is, down below the model itself is an interface pane. And what it's saying is that I've applied an interface to both the client and the service. So again, just to recap, the service component is connected to the main app through a service connector between the client port and the service port.

      Now, if we zoom in to that service interface just a little bit more, you'll see that I have a service interface name, which is different than the service connector name. I also have a full functional prototype. So I just went in and typed in my output equals my function with my inputs in parentheses. And so I created this function prototype. And as soon as I hit enter, it gave me my inputs and my outputs below in terms of which I can define more in a datatype, dimensions, units, min/max, et cetera.

      So now I can apply the full-service interface in terms of the function prototype and its arguments. And what this will do for me is it will allow me to build a skeleton model or simulate behavior model that has the interface constructs already in it. Now, before I can do that, I'm going to apply this to every service component that I haven't done yet here. I had five more service interfaces to create and this interface pane.

      But once I have that complete, now the real magic happens. I can take that service component. And if I Right-Click on it, it will create what we call a Simulink Behavior Model. Now, a Simulate Behavior Model, in this case, is a skeleton model or shell model that has the interfaces applied to it. And when I do that to the service component that has the service interface already defined for it, I get a component model or service component model that got created for me.

      And this model contains a simulating function. It also contains a new block called the function port. Now, that function port is what allows me to make sure I get connected to the service connector, which allows me to make sure I'm connected to the right calling service. I also have the service connector name, the Simulink function name. And in the Simulink function, I really have the full function prototype in front of me.

      Now, I also, inside of that Simulink function, I have an empty Simulink function with the input and output arguments. If I go one step further and just copy the actual original model contents from the original model and place them here in the Simulink function, I've really created my service component that has that functionality in it. So by doing this, I've just copy and pasted the original content into my Simulink function here.

      Now let's try that for the main app. So again, if I Right-Click on the main app and say Create Simulink Behavior, I get a Simulink Behavior Model. If we open that model as a top model, we get something that looks like this. So that's a far ways away from my original model. But let's see what it gave us.

      So it gave us all the inputs and outputs. In this case, we're using Bus Element Ports. But the way that we're using Bus Element Ports are analogous to Simulink in ports and Simulink out ports. It also gave us six triggered subsystems. And inside those triggered subsystems is a function call. Let's take a look at a little bit more detail what that looks like.

      So we have our triggered subsystem. We have our function port. Again, to make sure that we have the right connection from our call to our actual service. We have the service connector name and the Simulink function name. And inside of that triggered subsystem, we actually have a Simulink Caller Function Block. And that Simulink function caller has the actual service connector name and the Simulink function name, as well as the input and the output arguments. So now it's just a matter of wiring this thing up to look like our original model.

      So part of that process to make this skeleton model look like our original model is really to copy and paste the parts that we want to keep and alter the parts for the client caller functions that we want to change. And how that that's going to look like, again-- in the earlier explanation, I was talking about how we had some prefunctions or some conditioning functions off to the left of the diagram.

      We had our main body, our computation, which is in the green box. And then, in the blue box, we had more of a post algorithm. And if I look at, in the spirit of time, how I created that model, is I used some of those triggered subsystems to create more of a pre-, middle-, and post-type triggered functions. And if we look in more details, we can see that the pre-function really shows more of our function caller, very similar to how our original model looked like. But our original model had the true functional blocks in them, where here we're calling out to our services.

      Now, the middle part is the part that we wanted to leave alone. And the post part is one service that we're calling out to for, in this case, it's for torque vectoring. But this service is what we're calling out to here. And again, it's a caller function block that we've wired similar to the old model.

      OK, where do we end this refactoring process? Well, we end it where we started with. Instead of taking that test harness and applying it to the original model, now I want to take that test harness that I had that passed with my original regression test, and I want to apply it to this compositional model. In this case, I have a main application with six service components.

      And in this case, my tasks were able to pass. So that tells me that I didn't lose any functionality. And that's really important. I could refactor those tests now in terms of testing of the components and the smaller functions. But just to be able to live off of that regression test tells me I didn't lose any functionality.

      Now, we're going to touch on how we generate code. We're actually going to talk about the end-generated code. And in a longer class or workshop setting, we can actually go into more details about how we generated this code. But in this case, when I wanted to generate C++ that's agnostic to any platform. I'm using our ERT target. I have the ability to generate application code. So I'm taking the application component. I'm generating the C++.

      And in this case, you can see where I have my pre-algorithm functional body and where I actually call out to the service. In my service component, I have my service defined. And again, this is pretty good C++ code.

      Now, when I moved to the AUTOSAR adaptive platform, I'm going to get a lot more things in my C++ code. I'm going to get callouts to the ARA layer, which is more of a runtime environment for the adaptive platform. So you're going to see a lot of calls out to ARA. I'm also going to get a main.cpp file for every component that I have. And you'll see that in just a moment.

      But for the model, for the application model, here you can see that I'm generating my pre-algorithm function body. And if I scroll down further into this area, I have my callout to that one specific service. Now, in this case, I do have five other services that are being called out to just like I did in the other example.

      I can also see that here I have my Main.cpp file. And this is so that all the tasks that I need to have scheduled will get scheduled. So this is an important file. It's not just a trivial example. It's an important file to use. And in this case, I'm loading the executor up with my task-- my pre-algorithm, my main computation, and my post.

      When I go to my service component, you can see that I'm generating C++ code for my actual service function. And then I also have a Main.cpp. And if I scroll down further, I'm also loading the executor with my service.

      OK, so some key takeaways-- again, back to where we started. We have the tools to help you move to service-oriented architectures. We can show you how to generate the C++ code for service-oriented architecture-based components. We showed you a lot of information in the last few minutes. And again, we don't expect you guys to be subject matter experts, but we do hope that you'll engage with us on your service-oriented architectural projects. We are here to help.

      Some resources-- we definitely have a lot of web pages that talk about software-defined vehicle, service-oriented architectures. So this is where you can find more information. And where you can find more help is, we do offer good training and consulting services. Earlier, we talked about the System Composer Onramp. We also have a one-day training on System Composer. We have a three-day training in Embedded Coder.

      We have two different class offerings for AUTOSAR, a two-day class for classic, and then a consulting-led jumpstart program for adaptive that can be tailored to your needs. And then we also have consulting services that can take you from jumpstart to helping you deploy models to a service-oriented architecture in a seamless manner. So definitely reach out to us.

      The last slide I've got for you is that we're going to talk about the same content that we just talked about here in much more detail in our Novi office on June 20 from 1:30 to 3:30 PM. There's a QR code here. And if you have any questions about this class, definitely please reach out to me directly. I'd be happy to have you there. And then, with that, I'd like to say thank you for your attention, and have a great day.

      [APPLAUSE]

      View more related videos