{"id":197587,"date":"2025-11-05T11:09:08","date_gmt":"2025-11-05T11:09:08","guid":{"rendered":"https:\/\/innovationspace.ansys.com\/knowledge\/?post_type=topic&#038;p=197587"},"modified":"2025-11-20T15:04:57","modified_gmt":"2025-11-20T15:04:57","slug":"pyscadeone-from-model-to-automation","status":"publish","type":"topic","link":"https:\/\/innovationspace.ansys.com\/knowledge\/forums\/topic\/pyscadeone-from-model-to-automation\/","title":{"rendered":"PyScadeOne &#8211; From Model to Automation"},"content":{"rendered":"<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2025\/11\/scade-053-banner.jpeg\" style=\"max-height: 700px !important\" \/><br \/>\n    <em><\/em>\n<\/p>\n<h3  id=\"INTRODUCTION\">Introduction<\/h3>\n<p>At the heart of <strong>Ansys Scade One<\/strong> lies a model-based solution for developing embedded applicative software, enabling a seamless Model-Based Systems Engineering (MBSE) process. The main user interface is a graphical programming IDE, but under the hood, Scade One exposes a <strong>Python API<\/strong> called <strong>PyScadeOne<\/strong>, which we introduced in a previous article. It allows programmatic control over projects, jobs, simulations, code generation, and integration with external tools.<\/p>\n<p>In this article, we&#8217;ll show how PyScadeOne can be used to automate model verification, code generation, and FMU exports. These activities are foundational for automated CI\/CD actions, which we will cover in a follow-up article.<\/p>\n<h3  id=\"PYSCADEONE-ARCHITECTURE-OVERVIEW\">PyScadeOne Architecture Overview<\/h3>\n<p>First, let&#8217;s look at what we&#8217;re working with. The PyScadeOne package follows a clear, layered design:<\/p>\n<table style=\"max-width: 1000px\">\n<tr>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p><strong>Layer<\/strong><\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p><strong>Purpose<\/strong><\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p><strong>Key Classes<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p><strong>API Core<\/strong><\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p>Entry point, access to tools and projects<\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p><a href=\"https:\/\/scadeone.docs.pyansys.com\/version\/dev\/api\/scadeone.html\">ScadeOne<\/a><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p><strong>Project Management<\/strong><\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p>Handles .sproj content, modules, resources, and dependencies<\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p><a href=\"https:\/\/scadeone.docs.pyansys.com\/version\/dev\/api\/project.html\">Project<\/a><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p><strong>Model<\/strong><\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p>Contains Swan module and interface declarations<\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p><a href=\"https:\/\/scadeone.docs.pyansys.com\/version\/dev\/api\/model.html\">Model<\/a><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p><strong>Job Execution<\/strong><\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p>Defines jobs (.sjob), properties, and execution<\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p><a href=\"https:\/\/scadeone.docs.pyansys.com\/version\/dev\/api\/svc\/jobs.html\">Job, JobLauncher<\/a><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p><strong>Service Layer<\/strong><\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p>Provides services such as FMU export, Python wrappers, and simulation data<\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p><a href=\"https:\/\/scadeone.docs.pyansys.com\/version\/dev\/api\/svc\/index.html\">ansys.scadeone.core.svc.*<\/a><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p><strong>Interfaces<\/strong><\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p>Decouples mutual dependencies between core classes<\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p>IScadeOne, IProject<\/p>\n<\/td>\n<\/tr>\n<\/table>\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2025\/11\/scade-053-class-diagram.png\" style=\"max-height: 800px !important\" \/><br \/>\n    <em>PyScadeOne overview (click to enlarge)<\/em>\n<\/p>\n<h3  id=\"LAUNCHING-JOBS-WITH-PYSCADEONE\">Launching jobs with PyScadeOne<\/h3>\n<p>The <code>ScadeOne<\/code> object manages the lifecycle of all loaded projects and provides access to tool executables.<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow:auto;width:auto;background:none;border:none;padding:.2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\"><span><\/span><span style=\"font-weight: bold\">from<\/span> <span style=\"color: #555555\">ansys.scadeone.core.scadeone<\/span> <span style=\"font-weight: bold\">import<\/span> ScadeOne\r\n\r\n<span style=\"color: #999988;font-style: italic\"># Initialize using the installation directory<\/span>\r\nsone <span style=\"font-weight: bold\">=<\/span> ScadeOne(<span style=\"color: #bb8844\">r&quot;C:\\Program Files\\Ansys Inc\\vxxx\\Scade One&quot;<\/span>)\r\n\r\n<span style=\"color: #999988;font-style: italic\"># Load an existing project<\/span>\r\nproject <span style=\"font-weight: bold\">=<\/span> sone<span style=\"font-weight: bold\">.<\/span>load_project(<span style=\"color: #bb8844\">&quot;MySystem.sproj&quot;<\/span>)\r\n\r\nproject<span style=\"font-weight: bold\">.<\/span>load_jobs()\r\n\r\n<span style=\"color: #999988;font-style: italic\"># Inspect jobs<\/span>\r\n<span style=\"font-weight: bold\">for<\/span> job <span style=\"font-weight: bold\">in<\/span> project<span style=\"font-weight: bold\">.<\/span>jobs:\r\n\u00a0 \u00a0 <span style=\"color: #999999\">print<\/span>(job<span style=\"font-weight: bold\">.<\/span>name, job<span style=\"font-weight: bold\">.<\/span>_kind)\r\n<\/pre>\n<\/div>\n<p>It exposes:<\/p>\n<ul>\n<li><strong>Installation path resolution<\/strong> via <code>install_dir<\/code><\/li>\n<li><strong>Project management<\/strong>: <code>load_project()<\/code>, <code>new_project()<\/code><\/li>\n<li><strong>Tools<\/strong>, e.g. <code>sone.tools.job_launcher<\/code> to invoke the external job launcher<\/li>\n<li><strong>Context management<\/strong> for safe use with <code>with<\/code> blocks<\/li>\n<\/ul>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow:auto;width:auto;background:none;border:none;padding:.2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\"><span><\/span><span style=\"font-weight: bold\">with<\/span> ScadeOne(<span style=\"color: #bb8844\">&quot;C:\\Program Files\\Ansys Inc\\vxxx\\Scade One&quot;<\/span>) <span style=\"font-weight: bold\">as<\/span> sone:\r\n\u00a0 \u00a0 prj <span style=\"font-weight: bold\">=<\/span> sone<span style=\"font-weight: bold\">.<\/span>load_project(<span style=\"color: #bb8844\">&quot;controller.sproj&quot;<\/span>)\r\n\u00a0 \u00a0 <span style=\"font-weight: bold\">...<\/span>\r\n<\/pre>\n<\/div>\n<p>Scade One projects are persisted on disk as <code>.sproj<\/code> files. Each of these is modeled by PyScadeOne as a <code>Project<\/code> object that encapsulates all dependencies, resources, and jobs.<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow:auto;width:auto;background:none;border:none;padding:.2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\"><span><\/span>project <span style=\"font-weight: bold\">=<\/span> sone<span style=\"font-weight: bold\">.<\/span>load_project(<span style=\"color: #bb8844\">&quot;MySystem.sproj&quot;<\/span>)\r\n\r\n<span style=\"color: #999988;font-style: italic\"># List available jobs<\/span>\r\nproject<span style=\"font-weight: bold\">.<\/span>load_jobs()\r\n<span style=\"font-weight: bold\">for<\/span> job <span style=\"font-weight: bold\">in<\/span> project<span style=\"font-weight: bold\">.<\/span>jobs:\r\n\u00a0 \u00a0 <span style=\"color: #999999\">print<\/span>(job<span style=\"font-weight: bold\">.<\/span>name, job<span style=\"font-weight: bold\">.<\/span>_kind)\r\n\r\n<span style=\"color: #999988;font-style: italic\"># Add a resource (e.g., simulation data)<\/span>\r\n<span style=\"font-weight: bold\">from<\/span> <span style=\"color: #555555\">ansys.scadeone.core.project<\/span> <span style=\"font-weight: bold\">import<\/span> ResourceKind\r\nproject<span style=\"font-weight: bold\">.<\/span>add_resource(ResourceKind<span style=\"font-weight: bold\">.<\/span>SIMULATION_DATA, <span style=\"color: #bb8844\">&quot;scenario.sd&quot;<\/span>, key_name<span style=\"font-weight: bold\">=<\/span><span style=\"color: #bb8844\">&quot;NominalCase&quot;<\/span>)\r\n\r\n<span style=\"color: #999988;font-style: italic\"># Save changes<\/span>\r\nproject<span style=\"font-weight: bold\">.<\/span>save()\r\n<\/pre>\n<\/div>\n<p>Key features include:<\/p>\n<ul>\n<li><strong>Dependency management:<\/strong> add or remove dependent projects (<code>add_dependency()<\/code>, <code>remove_dependency()<\/code>)<\/li>\n<li><strong>Resource management:<\/strong> register simulation data, header, and source files<\/li>\n<li><strong>Model integration:<\/strong> internally binds to <code>ansys.scadeone.core.model.Model<\/code><\/li>\n<li><strong>Job loading:<\/strong> reads <code>.sjob<\/code> descriptors and instantiates <code>Job<\/code> objects via a factory<\/li>\n<\/ul>\n<p>In Scade One, Jobs define <em>actions<\/em> that a project can perform: model checking, simulation, code generation, and test execution.<\/p>\n<p>All jobs derive from the abstract base class <code>Job<\/code>.<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow:auto;width:auto;background:none;border:none;padding:.2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\"><span><\/span><span style=\"font-weight: bold\">from<\/span> <span style=\"color: #555555\">ansys.scadeone.core.job<\/span> <span style=\"font-weight: bold\">import<\/span> JobType\r\n\r\njob <span style=\"font-weight: bold\">=<\/span> project<span style=\"font-weight: bold\">.<\/span>get_job(<span style=\"color: #bb8844\">&quot;CodeGen_Main&quot;<\/span>)\r\n<span style=\"color: #999999\">print<\/span>(job<span style=\"font-weight: bold\">.<\/span>_kind <span style=\"font-weight: bold\">==<\/span> JobType<span style=\"font-weight: bold\">.<\/span>CODE_GENERATION) \u00a0<span style=\"color: #999988;font-style: italic\"># True<\/span>\r\n\r\n<span style=\"color: #999988;font-style: italic\"># Execute the job<\/span>\r\nresult <span style=\"font-weight: bold\">=<\/span> job<span style=\"font-weight: bold\">.<\/span>run()\r\n<span style=\"color: #999999\">print<\/span>(result<span style=\"font-weight: bold\">.<\/span>code, result<span style=\"font-weight: bold\">.<\/span>message)\r\n<\/pre>\n<\/div>\n<p>Job Types are as follows:<\/p>\n<table style=\"max-width: 1000px\">\n<tr>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p><strong>Type<br \/>\n<\/strong>(<code>JobType<\/code> enum)<\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p><strong>Class<br \/>\n<\/strong><\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p><strong>Purpose<br \/>\n<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p><code>MODEL_CHECK<\/code><\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p><code>ModelCheckJob<\/code><\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p>Validate model syntax and semantics<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p><code>CODE_GENERATION<\/code><\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p><code>CodeGenerationJob<\/code><\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p>Generate C source code<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p><code>SIMULATION<\/code><\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p><code>SimulationJob<\/code><\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p>Run model-based simulation<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p><code>TEST_EXECUTION<\/code><\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p><code>TestExecutionJob<\/code><\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px\">\n<p>Execute defined test jobs<\/p>\n<\/td>\n<\/tr>\n<\/table>\n<p>Each job has a <code>.properties<\/code> object mirroring the <code>.sjob<\/code> file fields.<br \/>\nFor example, <code>CodeGenerationJobProperties<\/code> includes <code>expansion<\/code>, <code>short_circuit_operators<\/code>, <code>use_macros<\/code>, etc.<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow:auto;width:auto;background:none;border:none;padding:.2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\"><span><\/span>job.properties.name_length = 255\r\njob.properties.keep_assume = True\r\njob.save() \u00a0 # writes updated .sjob file\r\n<\/pre>\n<\/div>\n<p>When <code>job.run()<\/code> is invoked:<\/p>\n<ul>\n<li>The job is wrapped into a <code>JobLauncher<\/code>.<\/li>\n<li>The external executable runs: <code>scade_one_job_launcher.exe run -p project.sproj -j JobName<\/code><\/li>\n<li>The process return code is captured and converted to a <code>JobResult<\/code>.<\/li>\n<\/ul>\n<p>With these components, a complete automation task in pure Python becomes straightforward, from running a model check to executing test jobs and exporting structured results.<\/p>\n<p>Now, let&#8217;s look at a couple of automation examples.<\/p>\n<h3  id=\"EXAMPLE-1-TEST-EXECUTION-WITH-JUNIT-EXPORT\">Example 1: test execution with JUnit export<\/h3>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow:auto;width:auto;background:none;border:none;padding:.2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\"><span><\/span><span style=\"font-weight: bold\">from<\/span> <span style=\"color: #555555\">ansys.scadeone.core.scadeone<\/span> <span style=\"font-weight: bold\">import<\/span> ScadeOne\r\n<span style=\"font-weight: bold\">from<\/span> <span style=\"color: #555555\">ansys.scadeone.core.job<\/span> <span style=\"font-weight: bold\">import<\/span> JobType, Job\r\n<span style=\"font-weight: bold\">import<\/span> <span style=\"color: #555555\">ansys.scadeone.core.svc.test.test_results<\/span> <span style=\"font-weight: bold\">as<\/span> <span style=\"color: #555555\">tr<\/span>\r\n<span style=\"font-weight: bold\">from<\/span> <span style=\"color: #555555\">junitparser<\/span> <span style=\"font-weight: bold\">import<\/span> Error, Failure, JUnitXml, TestCase, TestSuite\r\n<span style=\"font-weight: bold\">from<\/span> <span style=\"color: #555555\">pathlib<\/span> <span style=\"font-weight: bold\">import<\/span> Path \u00a0 \u00a0\r\n<span style=\"font-weight: bold\">from<\/span> <span style=\"color: #555555\">datetime<\/span> <span style=\"font-weight: bold\">import<\/span> datetime\r\n\r\n<span style=\"font-weight: bold\">def<\/span> <span style=\"color: #990000;font-weight: bold\">build_failure_message<\/span>(ti: tr<span style=\"font-weight: bold\">.<\/span>TestItem, failure: Failure) <span style=\"font-weight: bold\">-&gt;<\/span> <span style=\"color: #999999\">str<\/span>: <span style=\"color: #a61717;background-color: #e3d2d2\">\u2026<\/span>\r\n    <span style=\"color: #999988;font-style: italic\"># Omitted for clarity<\/span>\r\n\r\n<span style=\"font-weight: bold\">def<\/span> <span style=\"color: #990000;font-weight: bold\">sone2junit<\/span>(sone_test_file: Path, junit_file: Path) <span style=\"font-weight: bold\">-&gt;<\/span> <span style=\"color: #999999\">str<\/span>: <span style=\"color: #a61717;background-color: #e3d2d2\">\u2026<\/span>\r\n    <span style=\"color: #999988;font-style: italic\"># Omitted for clarity<\/span>\r\n\r\n<span style=\"color: #999988;font-style: italic\"># Initialize Scade One environment<\/span>\r\n<span style=\"font-weight: bold\">with<\/span> ScadeOne(<span style=\"color: #bb8844\">&quot;C:\/Program Files\/ANSYS Inc\/v261\/Scade One&quot;<\/span>) <span style=\"font-weight: bold\">as<\/span> sone:\r\n\u00a0 \u00a0 project <span style=\"font-weight: bold\">=<\/span> sone<span style=\"font-weight: bold\">.<\/span>load_project(<span style=\"color: #bb8844\">&quot;myProject\/myProject.sproj&quot;<\/span>)\r\n\u00a0 \u00a0 project<span style=\"font-weight: bold\">.<\/span>load_jobs()\r\n\u00a0 \u00a0 \r\n\u00a0 \u00a0 <span style=\"color: #999988;font-style: italic\"># Select a test execution job defined in the project<\/span>\r\n\u00a0 \u00a0 job <span style=\"font-weight: bold\">=<\/span> project<span style=\"font-weight: bold\">.<\/span>get_job(<span style=\"color: #bb8844\">&quot;TestExecutionJob0&quot;<\/span>)\r\n\r\n\u00a0 \u00a0 <span style=\"font-weight: bold\">if<\/span> job<span style=\"font-weight: bold\">.<\/span>_kind <span style=\"font-weight: bold\">==<\/span> JobType<span style=\"font-weight: bold\">.<\/span>TEST_EXECUTION:\r\n\u00a0 \u00a0 \u00a0 \u00a0 <span style=\"color: #999999\">print<\/span>(<span style=\"color: #bb8844\">f&quot;Running test job: {<\/span>job<span style=\"font-weight: bold\">.<\/span>name<span style=\"color: #bb8844\">}&quot;<\/span>)\r\n\u00a0 \u00a0 \u00a0 \u00a0 result <span style=\"font-weight: bold\">=<\/span> job<span style=\"font-weight: bold\">.<\/span>run()\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 <span style=\"color: #999988;font-style: italic\"># Handle job result<\/span>\r\n\u00a0 \u00a0 \u00a0 \u00a0 <span style=\"font-weight: bold\">if<\/span> result<span style=\"font-weight: bold\">.<\/span>code <span style=\"font-weight: bold\">==<\/span> <span style=\"color: #009999\">0<\/span>:\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span style=\"color: #999999\">print<\/span>(<span style=\"color: #bb8844\">&quot;\u2705 Tests executed successfully.&quot;<\/span>)\r\n\u00a0 \u00a0 \u00a0 \u00a0 <span style=\"font-weight: bold\">else<\/span>:\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span style=\"color: #999999\">print<\/span>(<span style=\"color: #bb8844\">&quot;\u274c Some tests failed.&quot;<\/span>)\r\n\u00a0 \u00a0 \u00a0 \u00a0 <span style=\"color: #999999\">print<\/span>(result<span style=\"font-weight: bold\">.<\/span>message)\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 <span style=\"color: #999988;font-style: italic\"># Convert native test results to JUnit XML<\/span>\r\n\u00a0 \u00a0 \u00a0 \u00a0 sone_test_file <span style=\"font-weight: bold\">=<\/span> job<span style=\"font-weight: bold\">.<\/span>storage<span style=\"font-weight: bold\">.<\/span>path<span style=\"font-weight: bold\">.<\/span>parent <span style=\"font-weight: bold\">\/<\/span> <span style=\"color: #bb8844\">&quot;out&quot;<\/span> <span style=\"font-weight: bold\">\/<\/span> job<span style=\"font-weight: bold\">.<\/span>test_result_file \u00a0 \u00a0 \u00a0 \u00a0\r\n\u00a0 \u00a0 \u00a0 \u00a0 junit_message <span style=\"font-weight: bold\">=<\/span> sone2junit(sone_test_file, Path(<span style=\"color: #bb8844\">&quot;myProject\/junit_results.xml&quot;<\/span>))\r\n\u00a0 \u00a0 \u00a0 \u00a0 <span style=\"color: #999999\">print<\/span>(junit_message)\r\n<\/pre>\n<\/div>\n<p>This scripts first instantiates <strong>Scade One<\/strong> via the <code>ScadeOne<\/code> class, pointing to the tool installation directory. Then, it loads the <code>.sproj<\/code> file and queries available jobs. It selects a <strong>test execution job<\/strong> (<code>JobType.TEST_EXECUTION<\/code>), launches it with <code>job.run()<\/code>, and captures the results.<\/p>\n<p>The <strong>test result file<\/strong> produced by Scade One is then converted to the <strong>JUnit format<\/strong> using the <code>sone2junit()<\/code> utility. This enables immediate visualization in CI systems, such as GitHub Actions or Jenkins.<\/p>\n<p>The same script can therefore <strong>run, evaluate, and publish test outcomes<\/strong>, showing the integrated nature of the PyScadeOne library.<\/p>\n<p>Now that our script is built, we may wrap it up into an automated action, triggerable from a CI\/CD workflow. Here is a sample YAML configuration for GitHub Actions:<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow:auto;width:auto;background:none;border:none;padding:.2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\"><span><\/span><span style=\"color: #000080\">name<\/span>:<span style=\"color: #bbbbbb\"> <\/span>|\r\n<span style=\"color: #bbbbbb\">  <\/span><span style=\"color: #008080\">PyScadone Model Check <\/span>\r\n\r\n<span style=\"color: #000080\">description<\/span>:<span style=\"color: #bbbbbb\"> <\/span>|\r\n<span style=\"color: #bbbbbb\">  <\/span><span style=\"color: #008080\">This action demonstrates how to create a custom  Action using Python<\/span>\r\n<span style=\"color: #bbbbbb\">  <\/span><span style=\"color: #008080\">and the PyScadeOne API to perform model checks in a Scade One project.<\/span>\r\n\r\n<span style=\"color: #000080\">inputs<\/span>:<span style=\"color: #bbbbbb\"> <\/span>...\r\n\r\n\r\n<span style=\"color: #000080\">runs<\/span>:\r\n<span style=\"color: #bbbbbb\">  <\/span><span style=\"color: #000080\">steps<\/span>:\r\n<span style=\"color: #bbbbbb\">    <\/span>-<span style=\"color: #bbbbbb\"> <\/span><span style=\"color: #000080\">name<\/span>:<span style=\"color: #bbbbbb\"> <\/span><span style=\"color: #bb8844\">&quot;Install<\/span><span style=\"color: #008080\"> <\/span><span style=\"color: #bb8844\">Git<\/span><span style=\"color: #008080\"> <\/span><span style=\"color: #bb8844\">and<\/span><span style=\"color: #008080\"> <\/span><span style=\"color: #bb8844\">clone<\/span><span style=\"color: #008080\"> <\/span><span style=\"color: #bb8844\">project&quot;<\/span>\r\n<span style=\"color: #bbbbbb\">      <\/span><span style=\"color: #000080\">uses<\/span>:<span style=\"color: #bbbbbb\"> <\/span>actions\/checkout@v5\r\n\r\n<span style=\"color: #bbbbbb\">    <\/span>-<span style=\"color: #bbbbbb\"> <\/span><span style=\"color: #000080\">name<\/span>:<span style=\"color: #bbbbbb\"> <\/span><span style=\"color: #bb8844\">&quot;Set<\/span><span style=\"color: #008080\"> <\/span><span style=\"color: #bb8844\">up<\/span><span style=\"color: #008080\"> <\/span><span style=\"color: #bb8844\">Python&quot;<\/span>\r\n<span style=\"color: #bbbbbb\">      <\/span><span style=\"color: #000080\">uses<\/span>:<span style=\"color: #bbbbbb\"> <\/span>actions\/setup-python@v6\r\n<span style=\"color: #bbbbbb\">      <\/span><span style=\"color: #000080\">with<\/span>:\r\n\r\n<span style=\"color: #bbbbbb\">    <\/span>-<span style=\"color: #bbbbbb\"> <\/span><span style=\"color: #000080\">name<\/span>:<span style=\"color: #bbbbbb\"> <\/span><span style=\"color: #bb8844\">&quot;Install<\/span><span style=\"color: #008080\"> <\/span><span style=\"color: #bb8844\">dependencies&quot;<\/span><span style=\"color: #bbbbbb\"> <\/span>...\r\n\r\n<span style=\"color: #bbbbbb\">    <\/span>-<span style=\"color: #bbbbbb\"> <\/span><span style=\"color: #000080\">name<\/span>:<span style=\"color: #bbbbbb\"> <\/span><span style=\"color: #bb8844\">&quot;Run<\/span><span style=\"color: #008080\"> <\/span><span style=\"color: #bb8844\">model<\/span><span style=\"color: #008080\"> <\/span><span style=\"color: #bb8844\">check<\/span><span style=\"color: #008080\"> <\/span><span style=\"color: #bb8844\">using<\/span><span style=\"color: #008080\"> <\/span><span style=\"color: #bb8844\">PyScadeOne&quot;<\/span>\r\n<span style=\"color: #bbbbbb\">      <\/span><span style=\"color: #000080\">shell<\/span>:<span style=\"color: #bbbbbb\"> <\/span>python\r\n<span style=\"color: #bbbbbb\">      <\/span><span style=\"color: #000080\">run<\/span>:<span style=\"color: #bbbbbb\"> <\/span>|\r\n<span style=\"color: #bbbbbb\">        <\/span><span style=\"color: #008080\">from pathlib import Path<\/span>\r\n<span style=\"color: #bbbbbb\">        <\/span><span style=\"color: #008080\">import os<\/span>\r\n<span style=\"color: #bbbbbb\">        <\/span><span style=\"color: #008080\">from ansys.scadeone.core.scadeone import ScadeOne <\/span>\r\n<span style=\"color: #bbbbbb\">        <\/span><span style=\"color: #008080\">from ansys.scadeone.core.job import JobType, Job <\/span>\r\n<span style=\"color: #bbbbbb\">        <\/span>\r\n<span style=\"color: #bbbbbb\">        <\/span><span style=\"color: #008080\">with ScadeOne( ${{ inputs.scade-dir }} ) as sone: <\/span>\r\n<span style=\"color: #bbbbbb\">          <\/span><span style=\"color: #008080\">project = sone.load_project( ${{ inputs.project }} )<\/span>\r\n<span style=\"color: #bbbbbb\">          <\/span><span style=\"color: #008080\">project.load_jobs() <\/span>\r\n\r\n<span style=\"color: #bbbbbb\">          <\/span><span style=\"color: #008080\"># Run a model check job<\/span>\r\n<span style=\"color: #bbbbbb\">          <\/span><span style=\"color: #008080\">job = project.get_job( ${{ inputs.job-name }} ) <\/span>\r\n<span style=\"color: #bbbbbb\">          <\/span><span style=\"color: #008080\">if job._kind == JobType.MODEL_CHECK: <\/span>\r\n<span style=\"color: #bbbbbb\">            <\/span><span style=\"color: #008080\">print(f&quot;Running Model Check  job: {job.name}&quot;) <\/span>\r\n<span style=\"color: #bbbbbb\">            <\/span><span style=\"color: #008080\">result = job.run() <\/span>\r\n<span style=\"color: #bbbbbb\">        <\/span><span style=\"color: #008080\">   <\/span>\r\n<span style=\"color: #bbbbbb\">            <\/span><span style=\"color: #008080\">if result.code == 0:<\/span>\r\n<span style=\"color: #bbbbbb\">              <\/span><span style=\"color: #008080\">print(&quot;\u2705 Model check completed successfully.&quot;)<\/span>\r\n<span style=\"color: #bbbbbb\">            <\/span><span style=\"color: #008080\">else:<\/span>\r\n<span style=\"color: #bbbbbb\">              <\/span><span style=\"color: #008080\">print(&quot;\u274c Model check failed.&quot;)<\/span>\r\n<span style=\"color: #bbbbbb\">              <\/span><span style=\"color: #008080\"># Save check report in a folder specified by  output<\/span>\r\n<span style=\"color: #bbbbbb\">              <\/span><span style=\"color: #008080\">Path( ${{ inputs.output }} ).parent.mkdir(parents=True, exist_ok=True)<\/span>\r\n<span style=\"color: #bbbbbb\">              <\/span><span style=\"color: #008080\">shutil.copy(job.storage.path.parent \/ &quot;out&quot; \/ &quot;log.txt&quot;, ${{ inputs.output }} )<\/span>\r\n<span style=\"color: #bbbbbb\">              <\/span><span style=\"color: #008080\">message += &quot;\\n&quot; + f&quot;Model check report saved to { ${{ inputs.output }} }&quot;<\/span>\r\n\r\n<span style=\"color: #bbbbbb\">    <\/span>-<span style=\"color: #bbbbbb\"> <\/span><span style=\"color: #000080\">name<\/span>:<span style=\"color: #bbbbbb\"> <\/span><span style=\"color: #bb8844\">&quot;Upload<\/span><span style=\"color: #008080\"> <\/span><span style=\"color: #bb8844\">model<\/span><span style=\"color: #008080\"> <\/span><span style=\"color: #bb8844\">check<\/span><span style=\"color: #008080\"> <\/span><span style=\"color: #bb8844\">report&quot;<\/span>\r\n<span style=\"color: #bbbbbb\">      <\/span><span style=\"color: #000080\">uses<\/span>:<span style=\"color: #bbbbbb\"> <\/span>actions\/upload-artifact@v4\r\n<span style=\"color: #bbbbbb\">      <\/span><span style=\"color: #000080\">with<\/span>:\r\n<span style=\"color: #bbbbbb\">        <\/span><span style=\"color: #000080\">name<\/span>:<span style=\"color: #bbbbbb\"> <\/span>model-check-report\r\n<span style=\"color: #bbbbbb\">        <\/span><span style=\"color: #000080\">path<\/span>:<span style=\"color: #bbbbbb\"> <\/span>${{ inputs.model-check-report }}\r\n<\/pre>\n<\/div>\n<p>This workflow initializes the <code>ScadeOne<\/code> class, loads the <code>.sproj<\/code> project file, retrieves the specified job, executes it via the PyScadeOne library, and collects results \/ artifacts into the pipeline&#8217;s workspace.<\/p>\n<h3  id=\"EXAMPLE-2-FMU-EXPORT\">Example 2: FMU export<\/h3>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow:auto;width:auto;background:none;border:none;padding:.2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\"><span><\/span><span style=\"font-weight: bold\">from<\/span> <span style=\"color: #555555\">ansys.scadeone.core.svc.fmu<\/span> <span style=\"font-weight: bold\">import<\/span> FMU_2_Export\r\n\r\nfmu <span style=\"font-weight: bold\">=<\/span> FMU_2_Export(project, <span style=\"color: #bb8844\">&quot;CodeGen_Main&quot;<\/span>)\r\nfmu<span style=\"font-weight: bold\">.<\/span>generate(kind<span style=\"font-weight: bold\">=<\/span><span style=\"color: #bb8844\">&quot;CS&quot;<\/span>, out_dir<span style=\"font-weight: bold\">=<\/span><span style=\"color: #bb8844\">&quot;out\/fmu&quot;<\/span>)\r\nfmu<span style=\"font-weight: bold\">.<\/span>build(with_sources<span style=\"font-weight: bold\">=True<\/span>)\r\n<\/pre>\n<\/div>\n<p>This script is much shorter. It uses the <code>FMU_2_Export<\/code> service to translate Scade One generated C code into a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Functional_Mock-up_Interface\">Functional Mock-up Unit<\/a> (<strong>FMU 2.0<\/strong>), for co-simulation or model exchange. This enables integration of Scade One models with other simulation frameworks or external systems.<\/p>\n<p>In the same way as the previous example, this can be wrapped into a CI\/CD action, to be automatically executed as part of a pipeline.<\/p>\n<h3  id=\"CONCLUSION\">Conclusion<\/h3>\n<p>In this article, we saw how the PyScadeOne API transforms Scade One from a desktop application into a <strong>programmable automation backend<\/strong><a href=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2025\/11\/scade-053-automation.gif\">.<\/a> Scripting is fully done from Python and does not depend on the GUI. It is <strong>extensible<\/strong>, letting users build their own workflows and services. It outputs structured job and results objects that are easily machine-readable. This allows easy <strong>integration<\/strong> with other workflows, such as CI\/CD pipelines.<\/p>\n<p>By programmatically creating, running, and exporting Scade One projects, developers can bring <strong>high-assurance modeling<\/strong> into the <strong>modern DevOps ecosystem<\/strong>. As a note, other Ansys products also have their PyAnsys libraries, making it very easy to integrate those products and their outputs into custom automated pipelines.<\/p>\n<p>Stay tuned for a future article where we will showcase how to leverage PyScadeOne to create an off-the-shelf library of GitHub Actions.<\/p>\n<p>In the meantime, if you are a SCADE user, you may access Scade One Essential with your existing licenses on the <a href=\"https:\/\/download.ansys.com\">Ansys Download Portal<\/a>. You may also schedule a live demo of Scade One using this <a href=\"https:\/\/www.ansys.com\/products\/embedded-software\/request-assessment-scade-one\">link<\/a>.<\/p>\n<h3  id=\"ABOUT-THE-AUTHOR\">About the author<\/h3>\n<table style=\"max-width: 1000px;border: none !important\">\n<tr>\n<td style=\"padding: 0px 10px;min-width: 150px;border: none !important\">\n<p style=\"text-align: center\">\n    <img decoding=\"async\" src=\"https:\/\/innovationspace.ansys.com\/knowledge\/wp-content\/uploads\/sites\/4\/2025\/11\/scade-053-author.png\" style=\"max-height: 150px !important\" \/><br \/>\n                <em><\/em>\n<\/p>\n<\/td>\n<td style=\"padding: 0px 10px;min-width: 150px;border: none !important\">\n<p><strong>Ludovic Oddos<\/strong> (<a href=\"https:\/\/www.linkedin.com\/in\/ludovicoddos\/\">LinkedIn<\/a>) is a Lead Product Specialist at Ansys. He has been supporting SCADE field engagements, in many industries, for more than 15 years. He has deep expertise in embedded software and its integration into various target environments.<\/p>\n<\/td>\n<\/tr>\n<\/table>\n","protected":false},"template":"","class_list":["post-197587","topic","type-topic","status-publish","hentry","topic-tag-pyscadeone","topic-tag-scade-one"],"aioseo_notices":[],"acf":[],"custom_fields":[{"0":{"_edit_lock":["1763677036:1769"],"_edit_last":["1769"],"_aioseo_title":[null],"_aioseo_description":[null],"_aioseo_keywords":["a:0:{}"],"_aioseo_og_title":[""],"_aioseo_og_description":[""],"_aioseo_og_article_section":[""],"_aioseo_og_article_tags":["a:0:{}"],"_aioseo_twitter_title":[""],"_aioseo_twitter_description":[""],"filter_by_optics_product":["Lumerical"],"_filter_by_optics_product":["field_64fb192ba3121"],"application_name":[""],"_application_name":["field_64a80903c8e15"],"family":[""],"_family":["field_64a809229a857"],"siebel_km_number":[""],"_siebel_km_number":["field_63ecbffce60db"],"salesforce_km_number":[""],"_salesforce_km_number":["field_63ecc018e60dc"],"km_published_date":[""],"_km_published_date":["field_64c77704499dd"],"product_version":[""],"_product_version":["field_64c776cb4fd2e"],"_bbp_forum_id":["27825"],"_bbp_topic_id":["197656"],"_bbp_author_ip":["192.104.24.227"],"_bbp_last_reply_id":["0"],"_bbp_last_active_id":["197588"],"_bbp_last_active_time":["2025-11-05 11:09:08"],"_bbp_reply_count":["0"],"_bbp_reply_count_hidden":["0"],"_bbp_voice_count":["0"],"_btv_view_count":["340"],"_bbp_likes_count":["4"]},"test":"solution"}],"_links":{"self":[{"href":"https:\/\/innovationspace.ansys.com\/knowledge\/wp-json\/wp\/v2\/topics\/197587","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/innovationspace.ansys.com\/knowledge\/wp-json\/wp\/v2\/topics"}],"about":[{"href":"https:\/\/innovationspace.ansys.com\/knowledge\/wp-json\/wp\/v2\/types\/topic"}],"version-history":[{"count":11,"href":"https:\/\/innovationspace.ansys.com\/knowledge\/wp-json\/wp\/v2\/topics\/197587\/revisions"}],"predecessor-version":[{"id":197656,"href":"https:\/\/innovationspace.ansys.com\/knowledge\/wp-json\/wp\/v2\/topics\/197587\/revisions\/197656"}],"wp:attachment":[{"href":"https:\/\/innovationspace.ansys.com\/knowledge\/wp-json\/wp\/v2\/media?parent=197587"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}