Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Integrating AF & LVOOP with hardware

Solved!
Go to solution

@WavePacket wrote:
One of the items that I've developed in the past is how to load a SQL/SQLite database recordset into LabVIEW. At the moment, my idea was LabVIEW variant attributes. To make the code separate from the structure of the database, so far I've been using database variants -- with their associated runtime errors.

Note that the MySQL/PostgreSQL/SQLite all support JSON, complete with features like searchability by subitems in the JSON.  Using JSON avoids type-mapping at the boundary of the database.

0 Kudos
Message 71 of 88
(1,650 Views)

@drjdpowell wrote:

@WavePacket wrote:
One of the items that I've developed in the past is how to load a SQL/SQLite database recordset into LabVIEW. At the moment, my idea was LabVIEW variant attributes. To make the code separate from the structure of the database, so far I've been using database variants -- with their associated runtime errors.

Note that the MySQL/PostgreSQL/SQLite all support JSON, complete with features like searchability by subitems in the JSON.  Using JSON avoids type-mapping at the boundary of the database.


Very interesting. So if the database supports a JSON field, you can store the JSON string in that field (potentially useful tutorial link). Then in LabVIEW, you can pull the JSON string from that aforementioned field and then use LabVEIW's Unflatten From JSON Function (link). The output of that VI is a cluster with all the type information autopopualted.

 

^At a conceptual level, is that it?


------------------------------------------------------------------------------------

Please join the conversation to keep LabVIEW relevant for future engineers. Price hikes plus SaaS model has many current engineers seriously concerned...

Read the Conversation Here, LabVIEW-subscription-model-for-2022
0 Kudos
Message 72 of 88
(1,645 Views)

@WavePacket wrote:
Very interesting. So if the database supports a JSON field, you can store the JSON string in that field (potentially useful tutorial link). Then in LabVIEW, you can pull the JSON string from that aforementioned field and then use LabVEIW's Unflatten From JSON Function (link). The output of that VI is a cluster with all the type information autopopualted

LabVIEW's inbuilt JSON feature is quite limited.  Try JSONtext instead.  Applications often are made up of multiple components, each with their own configuration.  Rather than one monolithic cluster for all App config, think modularly, where the main actor pulls out the configurations of the subcomponents and passes it to them to interpret. This is something that the inbuilt JSON methods are not capable of doing, so I don't want you to be lead astray.


 

0 Kudos
Message 73 of 88
(1,632 Views)

 


@drjdpowell wrote:

Those arguments may have merit generally, but it's hard to see their application to configuration storage, where the job of the storage component is to save and recover whatever data structure was provided to it, without using or applying any checks to it.  What compile-time error could actually happen? 

Well, the code might pass a config cluster of a wrong type (this is a bug) when saving configuration. It would be saved without any runtime errors, but you will have an issue when loading it back on the next run. With strong type checking this would be caught at compile time.

 


@drjdpowell wrote:
And your component has to convert your strong-typed LabVIEW types to some weak-typed storage format at some point in order to save it, so you are exposed to run time errors there.

True. My preferred storage syntax is MGI Read Write Anything. It does have variants as inputs (on Write) and outputs (on Read). So I do have runtime error handling on 'unflattening' a config cluster, but it is encapsulated inside a method that returns a strictly typed configuration cluster.

 


@drjdpowell wrote:

Personally, I have all components return config info already converted to JSON, so the configuration storage code doesn't even have to do any conversion operation on the data.  All conversion is done inside the component itself.  Here one uses Strong typing, and this is the best place to actually deal with run-time errors.


I see. You are using strings with JSON payloads instead of variants to get around strong type checking ... Would you still use JSON internally if you have a requirement for storing configuration data in XML or name/value syntax ?

 

There are many ways to skin a cat. I am not saying that mine is 'better'. This is a typical engineering tradeoff - I am OK with increasing class code complexity to get the benefit of strong type checking in calling context. It looks like your approach results in simpler code but requires callers to perform more extensive runtime error handling.

 

This is how I normally do it (for Option #2 - when application configuration is loaded on startup and saved on shutdown) - easy to read and provides a strong type checking wrapper around a weakly typed function call:

 

Dmitry_0-1635279652726.png

 

0 Kudos
Message 74 of 88
(1,612 Views)

 


@drjdpowell wrote:

@Dmitry wrote:

 

I also know (from personal experience) that cutting corners results in programs that are harder to extend, scale and maintain.

 


To me, your description of the three cases of configuration handling sound hard to extend at least.  


Not quite sure what you mean by "hard to extend". I've did Option 1 & Option 2 applications many times (and they constitute more than 90% of applications in my portfolio) without spending unreasonable time/effort when adding new features/classes, changing configuration typedefs for classes, etc. Option 3 is more complex. My advise would be sticking to Option #2 whenever possible ...

 

@drjdpowell wrote:

Configuration should be (and can be) extremely easy.

In the old days (before I discovered and started practicing SOLID Principles) configuration was easy. I was quite confident that classes should be self-sufficient:

 

  1. Composite classes should create and configure their subsystems (instances of other classes) without help from the outside
  2. Classes should support loading and saving their configurations from/to persistent storage of choice

I also put a lot of value in creating reusable code that could be shared across projects to make teams more productive.

 

Then I've read several books/articles by people smarter/more experienced than I am and found out that I was wrong on the above 🙂

 

Item #1 is a violation of Dependency Inversion Principle (D in SOLID)

Item #2 is a violation of Single Responsibility Principle (S in SOLID)

 

Both items substantially limit code reuse (by 'reuse' I mean using a class/module 'as is' in another project).

 

Example: you have a dozen classes (device drivers, etc.) being reused across multiple projects. Your default solution for storing configuration data is XML payload in INI files. Requirements for a new (but very similar) project explicitly require storing configuration in JSON format in a database

 

Would you be able to reuse existing classes in the new project ? My answer is - NO. You would need to create a new flavor for each class to address JSON/Database configuration storage requirement. This may be done in a number of ways - requiring different amount of effort (the simplest - creating a child class that overwrites the Load_Configuration/Save_Configuration methods only)

 

Having an application-specific Configuration Handler (Option #3) resolves this issue - it decouples classes from configuration storage syntax and location. This is the price you pay for keeping your reusable classes truly reusable. It is way easier for Options 1 & 2 since you can do configuration saving/loading directly in the Assembler class.

 

In my experience, there is a substantial net gain in productivity from making your modules/classes reusable - even if you have to implement a Configuration Handler for each of your projects (I either recycle it from another project or create it from a template in my reuse library)

 

 

0 Kudos
Message 75 of 88
(1,606 Views)

@Dmitry wrote:

 


@BertMcMahan wrote:

...


...3. Application configuration updates need to be saved/reloaded while the application is running. ...the design pattern and get example sample code (LV2015) here.

FYI I looked at that design pattern. It's stated motivation is "G classes support Single Inheritance only" -> so now that interfaces are supported in 2020+, perhaps the design pattern is no longer the easiest route to success for your #3?

 


------------------------------------------------------------------------------------

Please join the conversation to keep LabVIEW relevant for future engineers. Price hikes plus SaaS model has many current engineers seriously concerned...

Read the Conversation Here, LabVIEW-subscription-model-for-2022
0 Kudos
Message 76 of 88
(1,595 Views)

 


@WavePacket wrote:

@Dmitry wrote:

 


@BertMcMahan wrote:

...


...3. Application configuration updates need to be saved/reloaded while the application is running. ...the design pattern and get example sample code (LV2015) here.

FYI I looked at that design pattern. It's stated motivation is "G classes support Single Inheritance only" -> so now that interfaces are supported in 2020+, perhaps the design pattern is no longer the easiest route to success for your #3?

 


Well, its both - a yes and a no 🙂

 

YES: You do not need creating Façade classes to statically decouple calling code from methods/typedefs they do not use. LVOOP Interfaces is a better way to go starting with LV2020. So, practicing Interface Segregation Principle (ISP - the "I" in SOLID) now takes less effort.

 

NO: However, Configuration Handler still has to use Façade classes because Configuration Handler Object has to be a singleton - all callers using different Façade classes should be talking to the same instance of Configuration Handler and,  at the same time, be statically decoupled from Configuration Handler class. The latter is achieved by storing Configuration Handler object reference in each Façade Class object. Unfortunately, LVOOP Interfaces do not allow private data members (are stateless) and cannot be used in leu of LVOOP Classes in this case ...

 

I've been advocating for LVOOP Traits (essentially, Interfaces with State) for a long time, but LabVIEW R&D handed us 'Java 8'-style Interfaces - which is still better than what we had prior to LV2020. You can check my presentation on Traits - A New Approach to Designing Reusable Code (NIWeek 2019) for details ...

 

 

0 Kudos
Message 77 of 88
(1,588 Views)

@Dmitry wrote:

It looks like your approach results in simpler code but requires callers to perform more extensive runtime error handling.

 

This is how I normally do it...:

 

Dmitry_0-1635279652726.png

 


We'll, no, I don't have any more runtime error handling than you.  In your case, shown by your image, you have an application-level monolithic cluster of all config info.  All your sub components' config clusters are inside that large cluster.  Thus you need to match types between your components and you application-level config code.  You are using strong typing to do this, which is fine.  

 

However, I do not have this application-level code that is typed to the config info of the sub components.  There is no type-matching or type-conversion step to have any error.  A sub component's config is its config: correct by definition.  Runtime errors can happen in conversion from whatever gets loaded from disk, but that is the same for you.  So, "no", I don't have any more runtime error handling than you do.

0 Kudos
Message 78 of 88
(1,581 Views)

@Dmitry wrote:

 

Example: you have a dozen classes (device drivers, etc.) being reused across multiple projects. Your default solution for storing configuration data is XML payload in INI files. Requirements for a new (but very similar) project explicitly require storing configuration in JSON format in a database

 

Would you be able to reuse existing classes in the new project ? 


Real-world question: has this ever happened to you?  No client of mine has ever specified a particular storage type for config info.  If such a requirement occurred, I would first talk to my client to see if they really need such a requirement, and if so (unlikely) I would likely just make a json-to-whatever converter.  I don't see this as different than your case, where you need a LVCluster-to-whatever converter.  

0 Kudos
Message 79 of 88
(1,572 Views)

 


@drjdpowell wrote:

@Dmitry wrote:

 

Example: you have a dozen classes (device drivers, etc.) being reused across multiple projects. Your default solution for storing configuration data is XML payload in INI files. Requirements for a new (but very similar) project explicitly require storing configuration in JSON format in a database

 

Would you be able to reuse existing classes in the new project ? 


Real-world question: has this ever happened to you?  No client of mine has ever specified a particular storage type for config info.  If such a requirement occurred, I would first talk to my client to see if they really need such a requirement, and if so (unlikely) I would likely just make a json-to-whatever converter.  I don't see this as different than your case, where you need a LVCluster-to-whatever converter.  


Well, regardless of my 'real-world' answer it is still an SRP violation 🙂 As an Architect you can decide to either live with it or take steps to comply to SRP ...

 

Over the years I worked on a broad spectrum of projects:

 

  1. Stand-alone LabVIEW applications - I normally have full control of configuration storage syntax and methods/locations. If not - I can reason with my customers about that
  2. Stand-alone LabVIEW application (Test/Measurement/Control) running at larger customer sites along with other (unrelated) application. Customers either already have a LIMS system in place (or have a good chance to consider deploying one) that might impose constraints on configuration handling to make it more uniform/fit LIMS system implementation. I do not think you would have a good chance to negotiate configuration syntax/storage methods in such case
  3. A LabVIEW application that is a part of a much larger SW system to control a device/product : you may or may not be able to negotiate configuration syntax/storage methods in such case. One reason - simplifying configuration handling for the entire product by field engineers. It might be expensive to train these folks in the first place and having different configuration storage solutions for different product subsystems does not make it easier
  4. Supporting a line of products: such product lines evolve over multiple years (I have one that supports 4 or 5 models introduced over an 8 year span). Bigger customers may have their own LIMS systems and Marketing is normally open to their requests - making it quite likely you would need to provide an option (or multiple options) for specific customer configuration storage methods. A Configuration Handler Subsystem would be handy in such case as it is quite easy to encapsulate support for different configuration syntax/storage methods in a single location compared to updating multiple 'reusable' components in one's code base

As to 'json-to-whatever' converter vs 'LVCluster-to-whatever' converter: your choice depends on whether you are OK with weak type-checking or have a preference for strong type-checking.

 

0 Kudos
Message 80 of 88
(1,544 Views)