This is an automated email from the ASF dual-hosted git repository.
git-site-role pushed a commit to branch asf-site
in repository https://gitbox.apache.org/repos/asf/plc4x-website.git
The following commit(s) were added to refs/heads/asf-site by this push:
new 754df6014 Site checkin for project PLC4X: Jenkins Tools
754df6014 is described below
commit 754df6014e0a13422e7ef972a6365889d8156100
Author: jenkins <[email protected]>
AuthorDate: Mon Mar 4 05:26:42 2024 +0000
Site checkin for project PLC4X: Jenkins Tools
---
users/getting-started/plc4py.html | 251 ++++++++++++++++++++++++++++++++++++++
1 file changed, 251 insertions(+)
diff --git a/users/getting-started/plc4py.html
b/users/getting-started/plc4py.html
index 50aeda708..938134b76 100644
--- a/users/getting-started/plc4py.html
+++ b/users/getting-started/plc4py.html
@@ -322,7 +322,258 @@
<div class="sect1">
<h2 id="getting_started_with_python">Getting Started with Python</h2>
<div class="sectionbody">
+<div class="sect2">
+<h3 id="using_the_plc4py_api_directly">Using the PLC4PY API directly</h3>
+<div class="paragraph">
+<p>Currently, you need to install PLC4Py from the GitHub repository instead of
pypi.
+Once we have decided that PLC4Py is in a position to release we will publish
to pypi.</p>
+</div>
+<div class="paragraph">
+<p>Navigate to the sandbox/plc4py directory and run. This will install plc4py
in your global repository.</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre> pip install .</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>You now should be able to use PLC4Py in your application. A minimal example
is shown below.</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre>from plc4py.PlcDriverManager import PlcDriverManager
+
+connection_string: = "modbus://192.168.1.174:502";
+
+driver_manager = PlcDriverManager()
+async with driver_manager.connection(connection_string) as connection:
+ with connection.read_request_builder() as builder:
+ builder.add_item("Random Tag", "4x00001[10]")
+ request = builder.build()
+ future = connection.execute(request)
+ await future
+ response = future.result()</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>PLC4X generally supports a very limited set of functions, which is not due
to the fact, that we didn’t implement things, but that PLCs generally
support a very limited set of functions.</p>
+</div>
+<div class="paragraph">
+<p>The basic functions supported by PLCs and therefore supported by PLC4X
are:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p>Discover Devices (Not yet available for PLC4Py)</p>
+</li>
+<li>
+<p>List resources in the PLC</p>
+</li>
+<li>
+<p>Read data</p>
+</li>
+<li>
+<p>Write data (Not yet available for PLC4Py)</p>
+</li>
+<li>
+<p>Subscribe for data (Not yet available for PLC4Py)</p>
+</li>
+</ul>
+</div>
+<div class="paragraph">
+<p>In general, we will try to offer as many features as possible.
+So if a protocol doesn’t support subscription based communication it is
our goal to simulate this by polling in the background, so it is transparent
for the users (This simulation feature hasn’t been implemented yet
though, but it’s on our roadmap).</p>
+</div>
+<div class="paragraph">
+<p>But there are some cases in which we can’t simulate or features are
simply disabled intentionally:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p>If a PLC and/or protocol don’t support writing or browsing, we simply
can’t provide this functionality.</p>
+</li>
+</ul>
+</div>
+<div class="paragraph">
+<p>Therefore, we use metadata to check programmatically, if a given feature is
available.</p>
+</div>
+<div class="sect3">
+<h4 id="reading_data">Reading Data</h4>
+<div class="listingblock">
+<div class="content">
+<pre># Check if this connection support reading of data.
+if connection.is_read_supported():
+ logger.error("This connection doesn't support reading.")</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>As soon as you have ensured that a feature is available, you are ready to
build a first request.
+This is done by getting a <code>ReadRequestBuilder</code>:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre># Create a new read request:
+# - Give the single item requested an alias name
+with connection.read_request_builder() as builder:
+ builder.add_item("Random Tag 1", "4x00001[10]")
+ builder.add_item("Random Tag 2", "4x00011")
+ request = builder.build()</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>So, as you can see, you prepare a request, by adding tag addresses to the
request and in the end by calling the <code>build</code> method.</p>
+</div>
+<div class="paragraph">
+<p>If you are using the <code>BrowseApi</code> you might also have been
provided with <code>Tag</code> objects. In that case simply use
<code>addTag</code> and pass in the <code>Tag</code> object instead of the
address string.</p>
+</div>
+<div class="paragraph">
+<p>The request is sent to the PLC by issuing the <code>execute</code> method
on the request object:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre>try:
+ future = connection.execute(request)
+ await future
+ response = future.result()
+except TimeOutException:
+ # Handle timeout error
+except ...
+ # Handle all your other errors</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>In general, all requests are executed asynchronously.
+As soon as the request is fully processed, the callback gets called and will
contain a <code>ReadResponse</code>, if everything went right or an excception
if there were problems.</p>
+</div>
+<div class="paragraph">
+<p>The following example will demonstrate some of the options you have:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre>for tag_name in response.tag_names:
+ if response.tags[tag_name].response_code == PlcResponseCode.OK:
+ num_values: int = len(response.tags[tag_name].value)
+ # If it's just one element, output just one single line.
+ if num_values == 1:
+ logger.info("Value[" + tag_name + "]: " +
response.tags[tag_name].value)
+ else:
+ # If it's more than one element, output each in a single row.
+ logger.info("Value[" + tag_name + "]:")
+ for i in response.tags[tag_name].value.get_list():
+ logger.info(" - " + str(i))
+ else:
+ # Something went wrong, to output an error message instead.
+ logger.error("Error[" + tag_name + "]: " +
response.tags[tag_name].name())</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>In the for-loop, we are demonstrating how the user can iterate over the tag
aliases in the response.
+In case of an ordinary read request, this will be predefined by the items in
the request, however in case of a subscription response, the response might
only contain some of the items that were subscribed.</p>
+</div>
+<div class="paragraph">
+<p>Before accessing the data, it is advisable to check if an item was
correctly returned.
+This is done by the <code>response_code</code> property for a given alias.
+If this is <code>PlcResponseCode.OK</code>, everything is ok, however it could
be one of the following:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p>NOT_FOUND</p>
+</li>
+<li>
+<p>ACCESS_DENIED</p>
+</li>
+<li>
+<p>INVALID_ADDRESS</p>
+</li>
+<li>
+<p>INVALID_DATATYPE</p>
+</li>
+<li>
+<p>INTERNAL_ERROR</p>
+</li>
+<li>
+<p>RESPONSE_PENDING</p>
+</li>
+</ul>
+</div>
+<div class="paragraph">
+<p>Assuming the return code was <code>OK</code>, we can continue accessing the
data.</p>
+</div>
+<div class="paragraph">
+<p>As all PlcValue items support the len property, the user can check how many
items of a given type are returned by calling
len(response.tags[tag_name].value)</p>
+</div>
+<div class="paragraph">
+<p>You can then treat the values in the PlcList as a list using
response.tags[tag_name].value.get_list()</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="writing_data">Writing Data</h4>
+<div class="paragraph">
+<p>In general the structure of code for writing data is extremely similar to
that of reading data.</p>
+</div>
+<div class="paragraph">
+<p>So first it is advisable to check if this connection is even able to write
data:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre>// Check if this connection support writing of data.
+if not plc_connection.is_write_supported():
+ logger.error("This connection doesn't support writing.")
+ return</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>As soon as we are sure that we can write, we create a new
<code>PlcWriteRequest.Builder</code>:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre>// Create a new write request:
+// - Give the single item requested an alias name
+// - Pass in the data you want to write (for arrays, pass in a list of values)
+with connection.write_request_builder() as builder:
+ builder.add_item("Random Tag 1", "4x00001[2]", [1, 2])
+ builder.add_item("Random Tag 2", "4x00011", 1)
+ request = builder.build()</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>The same way read requests are sent to the PLC by issuing the
<code>execute</code> method on the request object:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre>try:
+ future = connection.execute(request)
+ await future
+ response = future.result()
+except TimeOutException:
+ # Handle timeout error
+except ...
+ # Handle all your other errors</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>As we don’t have to process the data itself, for the write request,
it’s enough to simply check the return code for each field.</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre>for tag_name in response.tag_names:
+ if response.tags[tag_name].response_code == PlcResponseCode.OK:
+ logger.info("Value[" + tag_name + "]: updated");
+ else:
+ # Something went wrong, to output an error message instead.
+ logger.error("Error[" + tag_name + "]: " +
response.tags[tag_name].name())</pre>
+</div>
+</div>
+</div>
+<div class="sect3">
+<h4 id="subscribing_to_data">Subscribing to Data</h4>
+<div class="paragraph">
+<p>Coming Soon</p>
+</div>
+</div>
+</div>
</div>
</div>
</main>