Friday, April 27, 2018

Kunnen de afstandbedieningen eindelijk in de vuilnisbak?

Vorig jaar schreef ik over een universele besturing van een auto.

Nu wil ik het hebben over een universele 'besturing' van de elektronische apparaten. We hebben in onze woonkamer een Samsung (Smart) TV, een Humax digitale onvanger en een Panasonic Soundbar. Ieder apparaat komt met een of meer afstandbedieningen.


Samsung TV Afstandbediening Samsung Smarthub afstandbediening
Humax afstandbediening Panasonic afstandbediening

De meeste leveranciers bieden applicaties aan, waarbij je door gebruik te maken van je mobiele telefoon of een tablet het multimedia apparaat kunt bedienen. Een probleem is echter dat hiervoor vaak een infrarood (IR) verbinding vereist is en deze nauwelijks nog in nieuwere telefoons beschikbaar is. 
Een mogelijke oplossing is de Broadlink M3 Mini.
Dit apparaat werkt als een IR verdeelstation dat via een Wi-Fi verbinding kan worden bediend. Als we in het bezit komen van dit apparaat hoe moet onze functionaliteit er dan op de telefoon uit zien om het gebruikersgemak te bieden om deze diverse set apparaten te kunnen bedienen?

Allereerst is er de vraag: welke functionaliteit is er nodig? Laten we de meest essentiele functies benoemen die op alle afstandbedieningen voorkomen:
- Aan- en uitschakelen van de apparaten
- Apparaatkeuze
- Volumecontrole
- Kanaalkeuze


Aan - en uitschakelen
Alle afstandbedieningen hebben een aan- en uitknop. Het domme is echter dat we altijd alle apparaten tegelijk aanzetten en ook waar allemaal tegelijk uitzetten. Dit doen we middels de Humax afstandbediening. We kiezen de apparaatknop en drukken hierna op de aan- en uitknop. 
Zou het niet veel eenvoudiger zijn als we alle apparaten tegelijk aan- en uit kunnen schakelen?

Apparaatkeuze
Drie van de vier apparaten hebben knoppen voor de apparaatkeuze. Een probleem is dat niet duidelijk is welk apparaat als laatste is gekozen. Hierdoor kan het zijn dat functionaliteit niet werkt. (Bijvoorbeeld de programma gids of de wisselknop voor TV/Radio als je het audio apparaat hebt gekozen).

Volume Controle
Bij gebruik van de TV zijn er diverse componenten voor het besturen van het geluid. Je zet het geluid op de Samsung TV zachter en regelt het geluid met audioknop voor de Panasonic Soundbar. Gebruik je echter Netflix via de TV Smarthub dan staat het volume veel te luid en moet je het volume minimaal halveren. Op basis van de kanaalselectie moet er een voorkeur instelling komen en de bediening van het volume moet intuitiver worden. 

Kanaalkeuze
Er zijn drie manieren om een TV- of Radio kanaal te kiezen:
  1. Voorkeurdrukknop
  2. Kanaalselectie vooruit- en achteruitknop
  3. Programmagids met 'scroll' knop
Bij de laatste twee opties, die binnen ons gezin redelijk vaak gebruikt worden, betekent dit  het veelvuldig indrukken van de knoppen om tot de uiteindelijke keuze te komen. Een nieuwe afstandbediening met meer gebruikersgemak moet dit anders kunnen bewerkstelligen..

Uitwerking
Hieronder is een ontwerpstudie voor een afstandbedieningsapplicatie. Het beginscherm bevat een minimaal aantal knoppen die eenvoudig geselecteerd kunnen worden. Het heeft een centrale aan- en uitknop waarmee alle apparaten kunnen worden aan of uitgeschakeld. Wanneer de apparaten aanstaan heeft het een vuurrode kleur. Als een of meer apparaten uitstaan is de aan- uitknop grijs. Ook het apparaaticoon wordt grijs afgebeeld. 
De beginstand is waar alle apparaten uitstaan. Deze situatie is hieronder afgebeeld.




Door middel van een apparaatknop kun je een specifiek apparaat selecteren. Deze situatie is hieronder afgebeeld.


De informatie op het scherm is specifiek voor het apparaat. Het biedt bijvoorbeeld ook de mogelijkheid om een individueel apparaat aan of uit te schakelen. In het voorbeeld van een TV geeft het de mogelijkheid om een programma te selecteren voor kijken of opname. De selectie maakt gebruik van de 'tilt' functionaliteit van het mobiele apparaat. Het voorover dan wel achterover bewegen selecteert het kanaal, door linksom- of rechtsom te draaien kies je de gewenste tijd. Het drukken op een specifiek blok geeft additionele informatie over het programma (niet afgebeeld).

Het bovenstaande is ontwikkeld op basis van de JBF (Jan-Boeren-Fluitjes) methodiek. Hoe sluit deze methodiek aan bij de bestaande theorieboeken; bijvoorbeeld 'Simple and Usable' van Giles Colborne?
Colborne noemt in zijn boek vier strategien om de gebruikersinterface te vereenvoudigen:
  • Verwijder. Verwijder alle onnodige knoppen totdat het apparaat teruggebracht is tot alleen de essentiele functionaliteit
  • Organiseer. Plaats knoppen in coherente groepen
  • Verberg. Verberg alle niet belangrijke knoppen zodat ze de gebruiker niet afleiden.
  • Verplaats. Maak een erg simpele interface met basis eigenschappen en beheers het overige met een menu op de TV. Hierbij wordt de complexiteit van de afstandbediening verplaatst naar de TV.
De interface volgens de JBF methode voldoet al aardig aan de strategieen van Colborne. De meeste knoppen op het beginscherm zijn al verwijderd (strategie 1), de knoppen zijn al georganiseerd op basis van het geselecteerde apparaat (strategie 2), Alle niet belangrijke knoppen zijn verborgen (strategie 3), hierbij moet worden aangetekend dat nog niet alle functionaliteit van de orginele afstandbediening beschikbaar is. 
De ontbrekende strategie is nummer 4. De programmagids zelf moet worden afgebeeld op de TV zelf. Dit heeft als bijkomend voordeel dat er geen internetverbinding noodzakelijk is voor het ophalen van de actuele programmagegevens op de bediening. Nog steeds hebben kunnen we de tilteigenschappen gebruiken, maar hebben we een extra knop nodig voor de informatiefunctie. Verder voegen we de volumeknoppen toe, zodat het TV volume hier apart kan worden ingeregeld.


 

Sunday, May 7, 2017

Hoe bestuur je een automatische auto?

Ik heb me altijd afgevraagd waar vind de besturing van een auto zijn oorsprong?

Zeker, in het begin werd de auto gezien als de mechanisatie van een koets, de eerste benzine auto van Carl Benz had in zijn gehele verschijning het uiterlijk van een koets, het paard was vervangen door de motor, de teugels door een stuur, of door hendels die je links- danwel rechtsaf konden laten gaan.

De teugels waren echter erg intuitief, je kon met een hulpmiddel het paard harder of zachter laten lopen, links- of rechtsaf laten gaan en laten stoppen. Een geoefende koetsier kon de teugels met een hand bedienen.

Als we nu, anno 2017,  in een moderne auto kijken dan heeft de auto nog steeds een stuurwiel om te kunnen sturen, een pedaal om te kunnen remmen, het versnellen moet vaak met een combinatie van schakelen (pookje + koppelpedaal) en gasgeven moet met een derde pedaal.

Dus de eenvoudige all-in-one besturing van 140 jaar terug is vervangen door 5 apparaten:
  • Stuurwiel
  • Versnellingspook
  • Koppelpedaal
  • Gaspedaal
  • Rempedaal
Alle bestuurders zijn getrained om deze apparaten te gebruiken, voor de wat oudere bestuurders zijn ze een automatisme, we hebben ons nooit afgevraagd: waarom doen we het zo? Hoe is deze situatie ontstaan en waarom wordt het nog steeds op deze manier gedaan, zowel op het gebied van mechanica als ook op het gebied van electronica zijn de afgelopen 150 jaar gigantische sprongen gemaakt die de noodzaak van al deze separate besturingcomponenten ter discussie stellen.

Ik vraag mij af hoe willen we een volledig automatische auto gaan besturen? De computer kan de auto volledig besturen, maar aan de andere kant willen we misschien nog het gevoel van het besturen van de auto. Ik persoonlijk zou het zeker waarderen als ik nog het idee heb dat ik in controle ben van de auto. De computer draagt zorg voor de veiligheid. Hij grijpt in op het moment dat we een ongeluk gaan maken of ons niet aan de verkeersregels houden.
Hoe ziet dan het bedieningspaneel van de toekomstige auto er dan uit? Lijkt het op het interieur van een bijvoorbeeld de Ferrari, zoals hierboven? Of wordt het meer zoals het concept van Audi
  


Welke functionaliteit verwachten we dan in de cockpit? In bepaalde aspecten vind ik het design van het Ferrari stuur mooi. Een afgeplat stuur geeft meer beenruimte, en is minder dominant aanwezig. Ik zou dus zeker gaan voor een afgeplat stuur.
Verder wil ik niet alles touch screen of voice gestuurd maken. Er zijn maar een enkele knoppen op het stuur die vanuit het stuur bediend kunnen worden. Sommige zaken kunnen gewoon zonder knoppen, maar volautomatisch, zoals licht, ruitenwissers, etc. Het centrale gedeelte van het stuur geeft de bestuurder informatie over zijn omgeving en de auto.
Het stuur is demontable, als er bestuurders zijn die een huiskamer idee willen hebben zonder stuur dan is dit mogelijk.

Het stuur reageert progressief, zodat slechts een maximale uitslag van ongeveer 45 graden nodig is. De weerstand van sturen is per bestuurder regelbaar, om een zo natuurlijk mogelijk stuurgevoel te geven.
De knoppen laten je snel tussen de diverse schermpjes navigeren. Mini-icoontjes van de diverse schermen zijn zichtbaar in het hoofdscherm.

Het versnellen of vertragen van de auto gebeurd door het stuur te kantelen. Dit is geheel traploos, zonder koppelen en er hoeft niet geschakeld te worden.

De vijf componenten zijn omvat in een intuitieve, gebruikersvriendelijke besturing.





Sunday, March 29, 2009

Enabling OPC information exchange using service bus (nServiceBus) part 2

In the previous post I created a simple message publisher to publish OPC tag information onto the nServiceBus service bus. In this post I’ll create a client that subscribes to the message and retrieves the message from the bus. The application is close to a copy of the publish/subscribe sample provide with the nServiceBus documentaion. Again the app.config can be used unchanged.

The first task is to amend the EventMessageHandler to use the event message class defined in the previous post.

using Messages;
using NServiceBus;
using System;

namespace Subscriber1
{
public class EventMessageHandler : IMessageHandler<EventMessage>
{
public void Handle(EventMessage message)
{
Console.WriteLine("Subscriber 1 received EventMessage with Id {0}.", message.EventId);
Console.WriteLine("Message time: {0}.", message.Time);
Console.WriteLine("Message duration: {0}.", message.Duration);
Console.WriteLine("Message description: {0}.", message.Description);
}
}
}


As you can see very straight forward.



The rest of the Subscriber1 can be used unchanged.



using System;
using Common.Logging;
using Messages;
using NServiceBus;

namespace Subscriber1
{
class Program
{
static void Main()
{
LogManager.GetLogger("hello").Debug("Started.");

var bus = NServiceBus.Configure.With()
.SpringBuilder()
.XmlSerializer()
.MsmqTransport()
.IsTransactional(false)
.PurgeOnStartup(false)
.UnicastBus()
.ImpersonateSender(false)
.SetMessageHandlersFromAssembliesInOrder(
typeof(EventMessageHandler).Assembly
)
.CreateBus()
.Start();

Console.WriteLine("Listening for events. To exit, press 'q' and then 'Enter'.");
while (Console.ReadLine().ToLower() != "q")
{
}
}
}
}



The implementation of second subscriber is similar. When running the program it shows the following result screen.



OPC and PubSub ESB output



You see the OpcEventMessageDispatcher publishes a message onto the bus which is read by both the subscribing clients.

Enabling OPC information exchange using service bus (nServiceBus) part 1

The samples in my previous post were pretty simplistic. In this post I want to extend the functionality and enable information exchange using a service bus. I’m using nServiceBus as the service bus implementation. A draft would look like:

OPC and PubSub ESB

The previously created program with the callback will listen for any changes on the tags. When a change occurs it will retrieve the values and publish them on the bus. Any client that subscribes to the message will receive the message. The nSeviceBus ESB has only a limited amount of prerequisites. Microsoft MSMQ needs to be enabled and some message queues create through a provided script.

For the exchange we need to define a message data class. For the sake of simplicity we will use the following format:

using NServiceBus;
using System;

namespace Messages
{
[Serializable]
public class EventMessage : IEvent
{
public Guid EventId { get; set; }
public DateTime Time { get; set; }
public TimeSpan Duration { get; set; }
public String Description { get; set; }
}

public interface IEvent : IMessage
{
Guid EventId { get; set; }
DateTime Time { get; set; }
TimeSpan Duration { get; set; }
String Description { get; set; }
}
}



Now I’ll use the previously create Tester class and rename it to OpcEventMessageDispatcher. I need to add a IBus member variable and change the Initialize method to also initialize the service bus. The Publish/subscribe sample of the nServiceBus provides sufficient infomation to do so. The with the sample provided application configuration file (app.config) can be used unchanged. The complete initialize method would look like:



private static IBus m_bus;



public void Initialize()
{
Opc.URL m_url = new Opc.URL("opcda://localhost/KEPware.KEPServerEx.V4");
m_server = new Opc.Da.Server(new OpcCom.Factory(), m_url);
try
{
if (m_server != null)
m_server.Connect();
}
catch (Opc.ConnectFailedException connectionFailure)
{
Console.WriteLine("Connection failure : " + connectionFailure.Message);
Console.WriteLine("Additional info : " + connectionFailure.InnerException.Message);
}
Console.WriteLine(String.Format("Connection with {0} successfull", m_server.Name));
m_bus = NServiceBus.Configure.With()
.SpringBuilder()
.MsmqSubscriptionStorage()
.XmlSerializer()
.MsmqTransport()
.IsTransactional(true)
.PurgeOnStartup(false)
.UnicastBus()
.ImpersonateSender(false)
.CreateBus()
.Start();
}
Now that we initialized the service bus we need to change the callback event to publish a message onto the bus.


//DataChange event
public void OnDataChange(object subscriptionHandle, object requestHandle, ItemValueResult[] values)
{

foreach (ItemValueResult item in values)
{
IEvent eventMessage = new EventMessage();
eventMessage.EventId = Guid.NewGuid();
eventMessage.Time = item.Timestamp;
eventMessage.Duration = TimeSpan.FromSeconds(99999D);
eventMessage.Description = String.Format("Tag {0} changed, current value {1} ", item.ItemName, item.Value);
m_bus.Publish(eventMessage);
Console.WriteLine("Published event with Id {0}.", eventMessage.EventId);
}
}


As you can see the creation is as easy as creating an event message and publish it using the Publish method. Running this would show following result:



OpcEventMessageDispatcher



The output shows the created messages with the Guid message id’s. In my next post I’ll create some clients that subscribe to the create messages.

Saturday, March 28, 2009

Retrieving tag information with Kepware & C# using asynchronous callbacks

 

In my previous post I managed to retrieve information out of production machines using the Kepware EX OPC server. In this post I’ll make a slightly more dynamic retrieval of the data. I will create again a retrieval for two tags, but the application should be “warned” whenever the tag value changes.

 

class Tester
{
private Opc.Da.Server m_server = null;
private Opc.Da.Subscription m_subscription = null;
private Opc.Da.SubscriptionState m_state = null;
private Opc.Da.Item[] m_items = null;

public void Initialize()
{
Opc.URL m_url = new Opc.URL("opcda://localhost/KEPware.KEPServerEx.V4");
m_server = new Opc.Da.Server(new OpcCom.Factory(), m_url) ;
try
{
if (m_server != null)
m_server.Connect();
}
catch (Opc.ConnectFailedException connectionFailure)
{
Console.WriteLine("Connection failure : " + connectionFailure.Message);
}
Console.WriteLine(String.Format("Connection with {0} successfull", m_server.Name));
}

public void QueryUsingDataChangedCallback()
{
m_state = new Opc.Da.SubscriptionState();
m_state.Name = "Data collector";
m_state.ServerHandle = null;
m_state.ClientHandle = Guid.NewGuid().ToString();
m_state.Active = true;
m_state.UpdateRate = 250; // Query every 250 ms
m_state.Deadband = 0;
m_state.Locale = null;

m_subscription = (Opc.Da.Subscription)m_server.CreateSubscription(m_state);
// Create room to observe 2 tags ...
m_items = new Item[2];
Opc.Da.Item m_item = new Item();
m_item.ClientHandle = Guid.NewGuid().ToString();
m_item.Active = true;
m_item.ItemName = "Channel_0_User_Defined.Ramp.Ramp4";
m_item.ItemPath = "";
m_item.ServerHandle = m_state.ClientHandle;
m_items[0] = m_item;
m_item = new Item();
m_item.ClientHandle = Guid.NewGuid().ToString();
m_item.Active = true;
m_item.ItemName = "Channel_1.Device_1.Tag_1";
m_item.ItemPath = "";
m_item.ServerHandle = m_state.ClientHandle;
m_items[1] = m_item; //Insert item
m_items = m_subscription.AddItems(m_items);
// Set the async callback event listener
m_subscription.DataChanged += new Opc.Da.DataChangedEventHandler(OnDataChange);
}


//DataChange event
public void OnDataChange(object subscriptionHandle, object requestHandle, ItemValueResult[] values)
{
foreach (ItemValueResult item in values)
{
Console.WriteLine("The observed data has changed ...");
Console.WriteLine("Item : {0} \nValue : {1}", item.ItemName, item.Value);
Console.WriteLine("Quality: {0}\nTime (HH:MM:SS mmmm) : {1}", item.Quality, item.Timestamp.ToString("HH:mm:ss ffff"));
}
}

static void Main(string[] args)
{
Tester tst = new Tester();
tst.Initialize();
tst.QueryUsingDataChangedCallback();
Console.ReadLine();
}
}
}


The QueryUsingDataChangeCallback method creates the tag group (subscription) and then creates the event handler to listen whenever values have changed. When ever the event occurs some message is printed.



KepwareCallbackOutput



Besites the milisecond time that’s a bit weird, the output shows that the tag values are observed and changes are shown. If we imagine that a some event control would be bound to this changes, we could create a windows WPF or Silverlight application that shows warning messages on to a display. Although that the code would fit for some major refactoring, I would like to dig in first to decouple this application from any other listening application or module. A tool that I assume can help me with the decoupling is the open source ESB nServiceBus. In a very schematic view it is shown in the next picture:



PublishSubscribeESB



Our previously build application would be re-factured to a messaging endpoint that publishes any tag value changes onto the service bus. All other clients that are interested in the information subscribe to the message and retreive the information from the service bus.

Gadget whish list

I subscribed to the 150k tour version of the Amstel Gold Race classic . Although the track will be routed properly,  I’m planning to buy a Garmin Edge s705 to track my cycling ride and any new ones. The new shortly (stable) available Tacx training software will support downloading the GPS data into my Fortius VR trainer. There are already cycling GPS download routes sites on the web (GPS routes Netherlands and  GPS routes Belgium).

Retrieving information from a “tag” using C# and Kepware OPC server

As promised on my previous post this post will show you how to connect and retrieve some data from a PLC connect through the Kepware EX server. The picture  shows the main screen of the  Kepware KEPServerEX

server. The structure is “channel”, “device” , “tag”. On the screen e.g. “Channel_0_User_Defined”, “Ramp”, “Ramp4”. For each tag the name, address, data type, scan rate, scaling and description are shown.

My first attempt will be to query just 2 tags on different channels and devices.

class Tester
{
private Opc.Da.Server m_server = null;

public void Initialize()
{
Opc.URL m_url = new Opc.URL("opcda://localhost/KEPware.KEPServerEx.V4");
m_server = new Opc.Da.Server(new OpcCom.Factory(), m_url) ;
try
{
if (m_server != null)
m_server.Connect();
}
catch (Opc.ConnectFailedException connectionFailure)
{
Console.WriteLine("Connection failure : " + connectionFailure.Message);
}
Console.WriteLine(String.Format("Connection with {0} successfull", m_server.Name));
}

public void QueryOPCTags()
{
Opc.Da.Item item = new Item();
Opc.Da.Item[] m_items = new Item[2]; // Define two elements
item.ItemName = "Channel_0_User_Defined.Ramp.Ramp4";
m_items[0] = item;
item = new Item();
item.ItemName = "Channel_1.Device_1.Tag_2";
m_items[1] = item;
ItemValueResult[] itemValues = null;
try
{
itemValues = m_server.Read(m_items);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
if (itemValues.Length > 0)
{
foreach (ItemValueResult value in itemValues)
{
Console.WriteLine("Item name:{0},value:{1},quality:{2}", value.ItemName, value.Value, value.Quality);
}
}
}


            static void Main(string[] args)
{
Tester tst = new Tester();
tst.Initialize();
tst.QueryOPCTags();


                Console.ReadLine();
}
}
}





The routine QueryOpcTags creates 2 tags and adds them to the tag array (m_items). It will then query the server to read the information from the defined tags and store the retrieved information in the ItemValueResult collection.



The result of running this piece of code will look like..



OPCTestOutputScreen



Although that the program is not very useful due to the static nature, it at least shows that we can get some information out of the “machine” using some C# code. In my next post I’ll extend the sample to retrieve information whenever a observed tag changes.