You have my +1 there : )
Am 12.05.20, 12:15 schrieb "Christofer Dutz" <[email protected]>:
Hi all,
so I think we have the C-API in a quite nice state now and I'd like to
merge the c-api branch back to develop ... it shouldn't have any negative
effect on the rest of the build. Just wanted to ask if you are cool with me
doing that.
Chris
Am 06.05.20, 11:05 schrieb "Christofer Dutz" <[email protected]>:
Hi all,
thanks to Otto's efforts now the C-API has it's equivalent to PlcValues
...
Also has the API sort of matured to some state I think we no longer
have to work on the "feature/c-api" branch and would like to merge this back to
develop.
In the end it's still in the sandbox alongside a lot of unfinished
stuff ... so we're not treating it as a first-class citizen yet.
What do you think?
Chris
Am 04.05.20, 19:44 schrieb "Christofer Dutz"
<[email protected]>:
Ok ... after several days of working in the garden a new day with
updates on PLC4C :-)
So today I managed to finish and commit some changes that perform a
full roundtrip of connection, read, disconnect using the "simulated" driver.
Right now that only parses the address strings and returns a random
int for every item .. this is not yet on-par with the java version of the
simulated driver, but more a preview of the API we are planning to use for
PLC4C.
All asynchronous operations:
- Connect
- Read
- Disconnect
Are implemented using something we call "system-tasks" which I
described in my last summary.
All seems to be working nicely and I really like the structure of
the code ... I know it will need quite a bit of cleaning up and I would be
super grateful, if some C professionals could review what I have created.
Constructive feedback is essential here for the continued work on this.
Chris
Am 29.04.20, 19:57 schrieb "Christofer Dutz"
<[email protected]>:
Aaaand another update :-)
So today I continued adding code-flesh to the empty API body.
I implemented two basic data-structures: List and Queue
(Noticing at the end that my queue isn't even needed).
Starting from today the functionality for: connecting, reading,
writing, etc. is implemented by callback functions generating so-called
system_tasks.
These are data-structures consisting of 4 properties:
- a state-machine-function callback
- an int representing the state-machine state the task is
currently in
- a pointer to a context data-structure (The state-machine
controls what's in there)
- a completed-flag that tells the system if a task is finished
So now if a driver for example is asked to connect, it
generates a system-task and that's added to the task-list.
Then as soon as the system_loop function is called, this
function goes through the list of queued system-tasks and for each calls the
state-machine function if comes with and passes in the task as an argument.
In the end the system loop checks If after executing the
state-machine-function the task is marked as "completed" ... if it is it
removes this task form the queue and continues with the next task in the list.
The cool thing is that this way we can even ensure the
system_loop function doesn't hog too much processing time ... so we could give
the loop a maximum execution time and as soon as that's exceeded the function
returns and the following tasks will be executed the next time the system_loop
function is called. In this case I would probably change the task-list into a
ring data-structure.
Right now my example hello world program if correctly loading
the "simulated" driver and a "dummy" transport and correctly connecting using
the above mechanisms.
Guess tomorrow I'll be writing string-parsers again to continue
working on implementing the read/write operations on the simulated-driver.
So far the update from today,
Chris
Am 28.04.20, 19:38 schrieb "Christofer Dutz"
<[email protected]>:
Hi folks,
even if this wasn't discussed as much as the other things
we discussed, I still think it's important.
So I was a strong advocate of the "promised land" ... I
wanted to use promises and register callbacks for async execution.
Theoretically this I cool ... however I had to notice it's
cool as long as you have someone cleaning up for you.
In most of the languages I encountered heavy usage of this
pattern there are garbage collection mechanisms in place and then this is a
great feature.
In C however this is not the case. Here you have to
manually free previously allocated memory and if you don't do that, you'll run
out of it pretty soon.
My main issue was that with promises it is difficulty to
explicitly clean them up as you have no means to see which part of the program
is still keeping a reference to it. If you clean that up and the other code
access it, the failure depends on your OS but in all cases it's pretty bad.
So I decided to undo the promises, I just introduced a few
days earlier.
To keep the code half-clean I decided to instead use some
manual state-machine like code.
Not much more to report besides the fact that I started
implementing the actual logic. Up to now all methods sort of just returned
empty or default objects. Now at least you can register drivers at the plc4c
system and create connection structures, that correctly parse plc4x connection
strings and lookup matching drivers in the registry.
Guess in the following days I'll have to work on how I can
actually define protocols in a way that I can have them processed in the
central "loop" method without blocking the OS.
Feel free to discuss here or on slack.
I'll continue to post summaries to what's been going on.
Chris
Am 23.04.20, 18:34 schrieb "Christofer Dutz"
<[email protected]>:
Hi,
today we worked quite a bit on making the directory
structure more C-folks-friendly.
So what we've now done, it we left the general
directory structure unchanged, but eliminated the maven-like structure.
So now every module has a "CMakeList.txt" file and a
"src", "include" and "test" directory.
"src" is pretty much what src/main/c and
src/main/resources would have been, just mixed.
Same applies for "test".
The directory structure now matches that of a lot of
other projects and it would even allow adding a second set of build
configurations to allow building PLC4C with MyNewts "newt" build tool (But
that's for the future ... no worries)
Today I also added a "simulated" driver which should
act similar like the one in PLC4J ... in the end it should respect the PLC4C
API and be used as some dummy driver which mainly helps with writing
hello_world applications as well as testing new integration modules.
Also did I extend the "system" with a function to
manually create and register drivers.
Especially in limited environments like embedded
systems, I think the typical: "I just add all drivers and load what I need"
doesn't work. Same as you can't just copy a driver to the device, a dynamic
search and discovery mechanism would be overkill. We will definitely add
similar loading mechanisms like in Java, but for now I'll work with manually
registering drivers to keep it simple, small and efficient.
So far the update for today.
Chris
Am 20.04.20, 16:41 schrieb "Christofer Dutz"
<[email protected]>:
Hi all,
currently we’re in a mode of synchronous exchange
on creating the PLC4C API on Apache slack “the-asf.slack.com” (Please send an
email if you need an invite).
We’re doing that as specially in the beginning this
simplifies and speeds up things drastically.
But as we’re at Apache and here “If it didn’t
happen on the list, it didn’t happen” I’m trying to keep the list in sync with
the outcome and give you folks the chance to participate by writing up
summaries.
So we have the great situation that Otto and Björn
have quite some experience in C and C++ and are being a huge help with getting
this started.
So a few weeks ago I have already setup a build
that’s integrated into our normal Maven build. However you need to enable to
profiles in order to do that:
* with-sandbox
* with-c
You need to run the maven build at least once as
our plc4x-maven-plugin is a maven plugin and we need to have it generate some
code first (or the build will fail)
The actual build however is done with CMake. This
allows us to use any CMake-capable IDE to develop PLC4C (I’m using CLion, but
VisualStudio seems to also work fine).
Today I created a branch feature/c-api as I think
I’ll sometimes be committing unbildable stuff in order to allow collaboration
(I commit something and others fix it ;-) ).
So please follow this branch.
Regarding the directory structure, we decided to
stick to a structure that is sort of aligned with the one we have in PLC4X as
this makes it easier to understand the structure (at least from a PLC4J point
of view). The structure should also not be too strange for a c developer. What
C developers might consider “an abomination” is that I started off using a
directory structure for modules which is maven-inspired.
The reason is that I think this allows to cleanly
separate prod from test code and code from resources (In this case code from
header files) … so a C module currently has the general structure:
CMakeLists.txt
src
- main
- c
- include
- test
- c
- include
The CMakeLists.txt file is sort of the equivalent
to our pom.xml … it tells CMake how to build the module. This is also where you
define “dependencies” … however in C you rather define the locations of the
header-files you intend on using … the actual using of code will happen by the
linker when compiling.
So now come the parts where I was super happy to
have some help with:
* In the API module we have a set of header
files which define the core functions and types
* However these types don’t really expose any
information on the internals of PLC4X. So even if you have a connection
structure, you can’t access any properties of this directly. If you want to
access any properties, you need to use the corresponding function. So if in the
“connection” domain you have a plc4c_connection structure, in order to access
the connection_string property of that, you need to call the
plc4c_connection_get_connection_string function and pass in your connection
object.
* As a currently implicit naming convention we
started using a prefix “plc4c_” for everything.
* We also split up the API functions into
domains like “system”, “connection”, … all the functions which then results in
names like “plc4c_system_create”. As we’re expecting the domains to grow, this
way we hope to keep the sizes of the code blocks manageable.
* While the interface a user would use is
defined in the API module, the implementation is then done in the SPI module
(pretty much like in PLC4J)
* Initially we started playing around with
callbacks, much like we’re doing in Java and as C does allow function pointers,
that seemed like a good idea. But people actually coding in C mentioned that
this form of coding feels very strange for full-blooded C developers. As we
want this API to feel native for C developers, we’ll go down this path.
* As C doesn’t support a try-catch-finally
style error handling, almost every function returns a return_code enum which
can be translated into error messages by using some helper functions. So it is
important to check if an operation returned OK and to do some error handling,
if this is not OK.
* The API will completely not use synchronous
operations. So we won’t provide a blocking “connect” function that returns as
soon as the connection is established, as some of the systems we’re going to
support are single threaded and don’t have any mutlitasking or scheduling.
Therefore we’ll have a “plc4c_system_loop()” function which is cyclically
called in order to do something.
So far the update … I hope I didn’t skip anything …
More will follow :-)
Chris