Jump to content

Blogs

Featured Entries

  • Cara Bereck Levy

    Roadmap 2017! Roundup 2016!

    By Cara Bereck Levy

    Hello - welcome back! The holidays are over, the New Year is here.
    Here at Unitronics headquarters, we are at work, setting up our development Road Map for 2017. As Unitronics users, you know that we pride ourselves on listening to our user community. And so, we ask you: What would you like Unitronics to develop in 2017?
    You can post suggestions to this blog, PM me, or email me at cara.levy@unitronics.com. Here is a summary of Unitronics major developments in 2016, many of which were the results of user requests – so, without further ado, I hereby present: The 2016 Unitronics Roundup! First, Hardware: New Samba models Samba now offers the TA22 configuration in 3.5", 4.3", and 7" screen sizes. ·        SM35/SM43/SM70 J-TA22 comprise 12 digital inputs, configurable as 1 hsc/shaft-encoder, 2 analog/digital, 2 thermocouple / pt100 inputs, and 8 pnp plus 2 analog outputs. ·        SM70-J-RA22 offers a 7" screen and the same inputs as the TA22; the outputs comprise 4 relay and 4 npn outputs. New Jazz models ·        JZ20-T20HS offers 6 digital, 2 analog/digital, 2 analog, 3 high-speed inputs, and 8 transistor, plus 5 high-speed outputs supporting PWM via hardware configuration. ·        JZ20-R16HS offers the same inputs, plus 6 Relay Outputs. UniStream additions UniStream welcomed new I/O Modules: ·        UIS-WCB2 Wide I/O comprises two temperature and 2 analog inputs, 2 analog outputs, and 10 digital inputs, 10 digital pnp outputs including 2 high-speed outputs. ·        Slim temp module UIS-4PTKN Comprising 4 RTD inputs that support PT1000/NI1000. KNX connector UniStream supports a KNX to MODBUS Gateway device, available as GW-KNX1.       Our Remote Operator mobile app for the Vision™ and Samba™ series, a major user request, was released in 2016—here are links for Apple and Android:
    https://itunes.apple.com/us/app/unitronics-remote-operator/id1063107386?mt=8 https://play.google.com/store/apps/details?id=com.unitronics.remoteoperator&hl=en           Here are some of the features we added to UniLogic in 2016:   User Access Control (UAC)
    Security UAC enables you to require users to log in via the HMI application, and restrict access to HMI elements, including whether an element is visible or enabled. SQL Connectivity ·        Access SQL databases via IP address or Hostname ·        Build SQL Queries and execute them via Ladder functions ·        Connect Data Tables to SQL databases and transfer data HMI Screen to Web Page Click to convert existing HMI Screens to Web Pages. Reusable HMI Custom Controls Create your reusable HMI Custom Control, drag and drop it from the Solution Explorer, export/import it between projects, and add it to the Library. Hardware Interrupt: UID-0808THS A new Scenario, Measure Length, supported by the UID-0808THS module, allows you to implement length measurement via hardware-based interrupt. HMI File Selector Browser Enable your operator to select files from an SD/DOK/memory stick, store the Path and Name of the selected file in a Data Tag. Video Streaming Widget: RSTP Allows you to provide the RSTP url in the format required by your particular application and device. HMI Indirect Images Display indirect images hosted on the SD card or DOK. HMI Trends: FIFO Display a Trend as Standard or FIFO. HMI email Configuration Widget Your users can add recipients in the To, Cc, and Bcc fields of an e-mail via the HMI keyboard. Web Server –  new features ·        Trends: view Trend graphs via web browser. ·        mailto fields, and a new widget enable your users to add recipients in the To, Cc, and Bcc fields of an e-mail via the HMI keyboard. ·        A new Web Data Tables Widget allows a user to view, edit, and enter data via web browser. Print to pdf The Tools tab on the UniLogic ribbon offers Print Project, enabling you to print your Hardware Configuration and Ladder Modules. Data Tables ·        New Functions: Convert .CSV to UDTF, UDTF Count counts number of columns and rows ·        email .csv files as attachments. MODBUS You can now run Modbus via Serial COM Module/USB ports Ethernet/IP ·        Explicit Messages support for both Scanner and Adapter in addition to I/O (Implicit) messaging. ·        Scanner Node support doubled, from 16 to 32. DNS Resolver Ladder Function: resolves a server IP address from its host name. UID-0808T
    Advanced Functions New functions for counters, Immediate Reset + new Scenarios UniLogic
    Advanced Layout Pin/unpin, undock and float pane options. If you use two monitors, float panes onto different monitor screens. USB keyboard support Just plug a keyboard into the UniStream USB port. Alarms - Additions ·        Language Localization for French, Russian, Polish, Finnish and Czech. ·        Alarm History Widget    
    • 5 comments
    • 829 views
  • Cara Bereck Levy

    MVPs 2016! Forum + Youtube roundup

    By Cara Bereck Levy

    This was an excellent year - a year of growth - for the Unitronics community. We now number over 20,000 members!
    I can back this up with hard statistics for 2016: We welcomed over 3,000 new members to our user forum… …who made 2,100 posts … in 526 new topic! We all benefit from forum members who devote big blocks of time to helping others. These people put a lot of effort into writing detailed answers to questions and aiding others in trouble-shooting their applications. This year, three of our top posters from 2016 were again top contributors to the Unitronics community. 
    Our top UniGuru MVPs for 2016 are: Joe Tauser Barry Lanier (forum name: Flex727) Ausman In recognition of their fine work, the profiles of our MVPs will be marked with a new icon for 2016: In recognition of their fine work, the profiles of our
    MVPs will be marked with a new icon for 2016: Our most valuable commodity is, always, time—to volunteer that time to help other people using your expertise is truly worthy of respect--thanks to you all! Youtube Channel  The official Unitronics Youtube channel, https://www.youtube.com/user/UnitronicsWebinars is growing. In 2016, our top video clips were the: Webinar: HMI Tricks and Tips for VisiLogic, with over 8,000 views:  
    https://www.youtube.com/watch?v=1MwhQkdbC6s PLC Training: Introduction to UniLogic, HMI + PLC Application Software, with over 6,000 views: 
    https://www.youtube.com/watch?v=BV30MkDQYPk WEBINAR: Unitronics Vision -  CANOpen, also with over 6,000 views:
    https://www.youtube.com/watch?v=iIkBJ4NheCc   Media -  We're in the NEWS! Unitronics was featured in a number of trade magazines in 2016—if you missed these articles, check 'em out now! IEN, Industry Interview feature:
    How Will Industry 4.0 Influence the Future of PLCs?
      IEN feature article. Hardware Division
    Benny Magrafta, Unitronics R&D Manager, Software Division, was extensively quoted:
    HMI checklist: What to consider when shopping for an HMI solution
      IEN - Industrial Products in Spotlight
    IEN featured an application based on the UniStream PLC + HMI, 10.4, a project done for Djurdjevic is a meat production company based in Serbia
    Is it possible for an automated system to run so efficiently you forget it’s there? We are proud to continue to bring you Unitronics true All-in-One value: our complete range of automation products and software from the pioneers of the PLC + HMI controller, backed by Unitronics unparalleled sales and tech support. Enjoy them in 2017!!    
    • 2 comments
    • 553 views
  • Cara Bereck Levy

    UniLogic Tech Tidbits - the Movie!

    By Cara Bereck Levy

    If you haven't checked out the Unitronics Youtube for awhile, you may want to check out our UniLogic Tech Tidbits Playlist. The most recent Tidbit shows you how to create your own, reusable HMI Custom Controls. After you create a control, you can drag and drop it from the Solution Explorer, export/import it between projects as .uluce files, or add it to the Library.  You can also define tags that are local to a specific Custom Control, these tags will be exported/imported along with the control.
      It's all in the link below!  
    • 0 comments
    • 528 views
  • Cara Bereck Levy

    Samba 7":- Control Engineering 2017 Award!

    By Cara Bereck Levy

    Six year winning streak! We are very proud to announce that Samba 7"   joins the ranks of Unitronics’ products that have been honored by Control Engineers’ Choice Award.  Samba won the award in the category of Hardware – integrated HMI controllers. Read more about it here.
       
    • 0 comments
    • 820 views

Our community blogs

  1. Let me tell you--this is going to be GREAT!!!!
    We are proud to introduce an addition to the UniStream family that is going to be a favorite among those of you who enjoy the compactness, the no-hassle approach of the All-in-One Vision series.

    UniStream 5" is a PLC+HMI+I/Os , with the impressive UniStream user panel built-in, available in a number of I/O configurations, that you program in UniLogic.

    Below is the official release document--you can access specs, etc, over here.

    UniStream® 5”: PLC+HMI+I/Os

    Compact. Contemporary. Connected

    UniStream 5.png

    Unitronics’ newly-released UniStream® 5"- a multi-function programmable logic controller in a superbly compact hardware profile: PLC+HMI+I/O built into a single unit.

    All-in-One, compact PLC + HMI with built-in I/Os fills a niche in the programmable controller market—serving OEMs and system integrator who need space-saving controllers that deliver the functionality to control complex machines, while creating a competitive advantage via a color-touch HMI panel.

    This All-in-One controller is available in two series: UniStream 5" and UniStream 5" Pro. Both series support SNMP, VNC, FTP, SMS, email, and communications via GSM/GPRS modem.

    The Pro version also offers a built-in Webserver, audio jack, and video support, as well as SQL, a plus for system integrators and OEMs facing Industry 4.0.

    The Hardware

    UniStream 5", with its resistive color touch-screen reduces space and wiring requirements, and eliminates the need to set up Panel-PLC communication.

    Built-in I/O configurations vary according to model, offering digital, analog, high-speed and temperature I/Os. Users can expand the configuration up to 2,048 I/O points.

    Built-in COM ports include Ethernet, 1 USB host and 1 Mini-B USB device ports; additional add-on ports for serial and CANbus are available.

    Powerful Programming

    The rich feature range of UniStream includes auto-tuned PID, data logging, data tables and Recipes, data sampling displayed via built-in Trends and Gauges, Alarms, multi-level passwords, and plug & play communications for CANopen, CAN Layer2, MODBUS, EtherNet/IP, and more.

    A Re-Use paradigm enables the programmer to build Ladder and C functions, design HMI screens, and create Web pages in multiple languages including Asian languages—then save it all in a Library to reuse in other projects.

    Another benefit to the bottom line for the OEM and system integrators is Unitronics standard policy of customer care—no hidden costs or tiers. All software, utilities, and support—including membership in Unitronics’ Community Forum—are provided at no additional charge

     

    • 1
      entry
    • 2
      comments
    • 1770
      views

    Recent Entries

    Hi all,

    This is an open invitation for you to suggest new ideas and thoughts for Unilogic UDFB's.

    It is very important for us to keep track of our customers needs. As part of this agenda we want to create and develop useful tools for your benefit.

    These UDFB's will be attached to the next releases of Unilogic.

    Please feel free to suggest any idea that seems useful.

    Thank you

    1.png?sfvrsn=0

  2. You are probably saying to yourself "Hey, you promised that the next episode of 'Build your own SD Card Explorer' will be published, so how come Saragani posts about something else?"Well, I actually said that the next episode will be posted next week :-)It is going to have a lot of code and it will be a lot of work writing it so I might split it to few pieces.Besides, I can use this blog entry to post about new features of the Com Driver.I wrote this multi listener a while ago, but the source code of the communication driver was not published since then.You might assume that writing the Multi-Listener was harder than writing the single listener, but it is actually the opposite..Net gives you an easy way to write a multi-listener and for having a single listener I had to manipulate the system to have just 1 connection (Lets say that in order to stop listening, I had to close the port, but then it caused the socket to be disposed so I had to create a new instance... and events from other threads and some other stuff sometimes caused Threads races).We didn't write the Multi Listener on the first place since we actually needed a single socket listener for the apps that use the communication driver, and we didn't extend the single listener to to multi listener because it will break the compatibility.Lets start:There are 2 new channels: ListenerServer and ListenerClient (Where ListenerClient have an internal constructor, cause it can only be created by the ListenerServer).ListenerServer is just like the EthernetListener, but it allows multiple connections (It means that several PLCs can connect to the PC on the same time and to the same port).Unlike the EthernetListener, the ListenerServer does not contain any logic for sending and receiving data. When communication is being accepted, the ListenerServer creates a new ListenerClient. If it manages to create a PLC object then it will be returned to the user by an event. The Plc will contain the ListenerClient.Few notes:1) The ListenerServer contains only 2 types of statuses: Listening and Disconnected (“Connected” is irrelevant since it is a property of the Client…)2) When a PLC object that uses a ListenerClient channel have the connection closed, the PLC object can’t be used anymore (It is being disposed).3) In the EthernetListener (The old Single connection Listener) calling PLC.Disconnect or ethernetListener.Disconnect closes the client connection. With the new ListenerServer PLC.Disconnect closes the connection of the specific PLC (Closes a specific client), and ListenerServer.Disconnect closes the Listen (The status changes from Listening to Disconnected).4) Just like before, if the Message Queue is not empty then Plc.Disconnect doesn’t close the connection. When using ListenerServer/ListenerClient, you can call PLC.Dispose which closes the connection no matter how many messages are in the queue.a. It is a good practice to Dispose a PLC if the ListenerClient is no longer needed/In use (To close unused sockets). b. When the PLC object has no references then it will be collected by the GC and therefore the ListenerClient will also be collected by the GC. The destructor of the ListenerClient currently does not contain any “Socket.Close” (This might change in the future). I believe that the socket will be closed when the channel will be collected by the GC, but you can never tell when the GC will decide to run.c. Dispose a PLC only when it’s ListenerClient is no longer needed. For example:

    PLC plc;        PLC plc2;        private void Listen()        {            try            {                PLCFactory.GetChannel(LOCAL_PORT, out listenerServer);                if (listenerServer == null)                    listenerServer = new ListenerServer(LOCAL_PORT, 3, 3000);                listenerServer.OnConnectionAccepted += new ListenerServer.ConnectionAcceptedDelegate(listenerServer_OnConnectionAccepted);                listenerServer.OnConnectionStatusChanged += new ListenerServer.ConnectionStatusChangedDelegate(listenerServer_OnConnectionStatusChanged);                PLCFactory.GetPLC(listenerServer);                listening = true;            }            catch (Exception ex)            {                MessageBox.Show(ex.Message);            }        }        void listenerServer_OnConnectionAccepted(PLC plcFromListener)        {            plc = plcFromListener;            plc2 = PLCFactory.GetPLC((plcFromListener.PLCChannel as ListenerClient),2); // Get the PLC on Unit ID 2 (Assuming that plcFromListener is Unit ID 1.            plcFromListener.Dispose(); // This will also cause plc2 to disconnect cause plc2 is using the same resource (The same ListenerClient)                    }

    However, if plc was created from one from of the OnConnectionAccepted event calls and plc2 was created from a different call of OnConnectionAccepted then those 2 PLC object will have different ListenerClient object, so disposing 1 PLC does not affect the other one.As I'm a fan of WPF and Binding, then I will write the example in WPF. It doesn't affect how you use the Com Driver, but it makes my life easier when the UI doesn't have any Business Logic.If I also change properties from another thread, then the UI will not crash since OnPropertyChanged protects me (it uses the Dispatcher in order to use the UI thread).If you are using WinForms then you will have to use this.BeginInvoke on the form etc...Lets start an MVVM solution.Since we support several clients then we will need a class that represents a single client (And we will have a list of all the clients in order to keep tracking on them and disconnect them if needed).I'll call this class ClientViewModel and it will inherit the ViewModelBase.

        public class ClientViewModel : ViewModelBase    {    }

    The Client will need to contain the PLC Object, we will want to show the IP of the client, the PLC Model, the OS Version and a value of an Operand (Lets take SDW 0 since it has an Auto Increment).I'll make it simple, I will have all those properties (Except the PLC object) as Strings).I also need a Timer that will Tick every once in a while and request the Value of SDW 0.

        public class ClientViewModel : ViewModelBase    {        private PLC plc;        private string clientIP;        private string plcModel;        private string osVersion;        private string plcName;        private string sdw0;        Timer timer;    }

    What I also need is a way to Disconnect, Run, Stop, Restart etc the PLC so I will use a DelegateCommand which is a class in the MVVM template that inherits from the ICommand Interface.I also need a way to tell the main class (MainViewModel) that the Client has disconnected, so I will use an Even in this case:

        public class ClientViewModel : ViewModelBase    {        private PLC plc;        private string clientIP;        private string plcModel;        private string osVersion;        private string plcName;        private string sdw0;        Timer timer;        private DelegateCommand disconnectClientCommand;        private DelegateCommand resetPlcCommand;        private DelegateCommand stopPlcCommand;        private DelegateCommand runPlcCommand;        public event EventHandler OnConnectionClosed;

    Now lets start adding the code to this class:

        public class ClientViewModel : ViewModelBase    {        private PLC plc;        private string clientIP;        private string plcModel;        private string osVersion;        private string plcName;        private string sdw0;        Timer timer;        private DelegateCommand disconnectClientCommand;        private DelegateCommand resetPlcCommand;        private DelegateCommand stopPlcCommand;        private DelegateCommand runPlcCommand;        public event EventHandler OnConnectionClosed;        public ClientViewModel(PLC plcFromListener)        {            plc = plcFromListener;            (plc.PLCChannel as ListenerClient).OnConnectionClosed += new ListenerClient.ConnectionClosedDelegate(ClientViewModel_OnConnectionClosed);            ClientIP = (plc.PLCChannel as ListenerClient).RemoteIP;            PlcVersion version = plc.Version;            PlcModel = version.OPLCModel;            OsVersion = version.OSVersion + "(" + version.OSBuildNum + ")";            Action action = delegate()            {                PlcName = plc.PlcName;            };            action.BeginInvoke(null, null);            timer = new Timer(100);            timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);            timer.Start();        }        void ClientViewModel_OnConnectionClosed(ListenerClient ethernetListener)        {            if (timer != null)                timer.Stop();            if (OnConnectionClosed != null)                OnConnectionClosed(this, null);        }        public string ClientIP        {            get            {                return clientIP;            }            internal set            {                clientIP = value;                OnPropertyChanged("ClientIP");            }        }        public string PlcModel        {            get            {                return plcModel;            }            internal set            {                plcModel = value;                OnPropertyChanged("PlcModel");            }        }        public string OsVersion        {            get            {                return osVersion;            }            internal set            {                osVersion = value;                OnPropertyChanged("OsVersion");            }        }        public string PlcName        {            get            {                return plcName;            }            internal set            {                plcName = value;                OnPropertyChanged("PlcName");            }        }        public string Sdw0        {            get            {                return sdw0;            }            internal set            {                sdw0 = value;                OnPropertyChanged("Sdw0");            }        }        public ICommand DisconnectClientCommand        {            get            {                if (disconnectClientCommand == null)                {                    disconnectClientCommand = new DelegateCommand(disconnectClient);                }                return disconnectClientCommand;            }        }        private void disconnectClient()        {            try            {                // When using a Listener Server, it is important to close the connection to the PLC                // if it's not needed anymore. plc.Disconnect closes the connection only if there are no                // pending messages in the queue (This is why it is important to abort the communication before disconnecting).                // However, plc.Dispose will allow you to close the connection anyway (if other PLC objects use the same connection                // which is the same ListenerClient) then they will become diposed as well.                // Dispose method is only relevant to ListenerClient connections.                plc.Dispose();            }            catch            {            }        }        public ICommand ResetPlcCommand        {            get            {                if (resetPlcCommand == null)                {                    resetPlcCommand = new DelegateCommand(resetPlc);                }                return resetPlcCommand;            }        }        private void resetPlc()        {            try            {                // Reseting the PLC will cause the connection to be closed.                // If the PLC is connected directly to the PC using a cable then the PC should get a disconnection event.                // However, if there is a hub in the middle the PC will get the disconnection event only after it tries to communicate with the PLC.                // In this example we read SDW 0 every 100ms which will cause the connection to be "tested" just like mentioned above.                plc.Reset();            }            catch            {            }        }        public ICommand StopPlcCommand        {            get            {                if (stopPlcCommand == null)                {                    stopPlcCommand = new DelegateCommand(stopPlc);                }                return stopPlcCommand;            }        }        private void stopPlc()        {            try            {                plc.Stop();            }            catch            {            }        }        public ICommand RunPlcCommand        {            get            {                if (runPlcCommand == null)                {                    runPlcCommand = new DelegateCommand(runPlc);                }                return runPlcCommand;            }        }        private void runPlc()        {            try            {                plc.Run();            }            catch            {            }        }        void timer_Elapsed(object sender, ElapsedEventArgs e)        {            try            {                ReadWriteRequest[] rw = new ReadWriteRequest[1];                rw[0] = new ReadOperands()                {                    NumberOfOperands = 1,                    OperandType = OperandTypes.SDW,                    StartAddress = 0,                };                plc.ReadWrite(ref rw);                Sdw0 = ((rw[0].ResponseValues as object[])[0]).ToString();            }            catch (ComDriveExceptions cde)            {                if (cde.ErrorCode == ComDriveExceptions.ComDriveException.ObjectDisposed)                {                    plc.Dispose();                }            }            catch (Exception ex)            {            }        }    }

    We are done with the ClientViewModel... Now we can start with the MainViewModel.We want a list of ClientViewModels, so we will use an ObservableCollection that can be easily binded to a ListBox or any ItemsContainer:

        public class MainViewModel : ViewModelBase    {        private ObservableCollection<ClientViewModel> clients = new ObservableCollection<ClientViewModel>();    }

    We need an option of the UI to tell the MainViewModel to Start or Stop Listening. We will again use a DelegateCommand.

        public class MainViewModel : ViewModelBase    {        private ObservableCollection<ClientViewModel> clients = new ObservableCollection<ClientViewModel>();        private DelegateCommand exitCommand;        private DelegateCommand listenCommand;        private DelegateCommand disconnectCommand;    }

    We also want a boolean that will tell the UI if we are Listening or not, we need a reference to the ListenerServer (The Server Channel that we have created) in order to tell it to Start Listening or Stop Listening.I'm going to have the port that we listen to as a Const:private const int LOCAL_PORT = 20275;I also need a Status (Ready, Listening, etc). Lets make it a String.And just one last thing:Observable Connection isn't thread safe, so if the collection is binded to the UI and we alter it from a different thread then we will get an Excetion.Therefore, we need a Dispatcher.The code is:

        public class MainViewModel : ViewModelBase    {        private ObservableCollection<ClientViewModel> clients = new ObservableCollection<ClientViewModel>();        private DelegateCommand exitCommand;        private DelegateCommand listenCommand;        private DelegateCommand disconnectCommand;        private bool listening = false;        private ListenerServer listenerServer;        private const int LOCAL_PORT = 20275;        private string status;        private Dispatcher dispatcher;        #region Constructor        public MainViewModel()        {            Status = "Ready";            dispatcher = Dispatcher.CurrentDispatcher;        }        #endregion    }

    Lets start addting some properties (Public properties with OnPropertyChanged:

    public ObservableCollection<ClientViewModel> Clients        {            get            {                return clients;            }            internal set            {                clients = value;                OnPropertyChanged("Clients");            }        }        public string Status        {            get            {                return status;            }            internal set            {                status = value;                OnPropertyChanged("Status");            }        }

    We also need to add the ICommand properties we make the program Exit, Listen or Disconnect (Close the Listener).

            public ICommand ExitCommand        {            get            {                if (exitCommand == null)                {                    exitCommand = new DelegateCommand(Exit);                }                return exitCommand;            }        }        private void Exit()        {            Application.Current.Shutdown();        }        public ICommand ListenCommand        {            get            {                if (listenCommand == null)                {                    listenCommand = new DelegateCommand(Listen, canListen);                }                return listenCommand;            }        }        private bool canListen()        {            return !listening;        }        private void Listen()        {            try            {                PLCFactory.GetChannel(LOCAL_PORT, out listenerServer);                if (listenerServer == null)                    listenerServer = new ListenerServer(LOCAL_PORT, 3, 3000);                listenerServer.OnConnectionAccepted += new ListenerServer.ConnectionAcceptedDelegate(listenerServer_OnConnectionAccepted);                listenerServer.OnConnectionStatusChanged += new ListenerServer.ConnectionStatusChangedDelegate(listenerServer_OnConnectionStatusChanged);                PLCFactory.GetPLC(listenerServer);                listening = true;            }            catch (Exception ex)            {                MessageBox.Show(ex.Message);            }        }        void listenerServer_OnConnectionStatusChanged(EthernetListener.ConnectionStatus connectionStatus)        {            // You can also use a timer to scan the status every, lets say, 1 second.            updateStatus(connectionStatus);        }        private void updateStatus(EthernetListener.ConnectionStatus connectionStatus)        {            string text = "";            switch (connectionStatus)            {                case EthernetListener.ConnectionStatus.Listening:                    text = "Listening, Connected Clients: " + getNumberOfClients().ToString();                    break;                case EthernetListener.ConnectionStatus.Disconnected:                    text = "Disconnected, Connected Clients: " + getNumberOfClients().ToString();                    break;            }            Status = text;        }        void listenerServer_OnConnectionAccepted(PLC plcFromListener)        {            // This event is not raised from the GUIs thread. Therefore we need to invoke it on the            // Main thread using the dispatcher. It is important to create the viewmodels in the GUI threa            // cause we want the dispatcher of the viewmodels to be associated with the right thread.            Action action = delegate()            {                ClientViewModel clientViewModel = new ClientViewModel(plcFromListener);                clientViewModel.OnConnectionClosed += new EventHandler(clientViewModel_OnConnectionClosed);                clients.Add(clientViewModel);                updateStatus(listenerServer.Status);                refreshIndexes();            };            dispatcher.Invoke(action, null);        }        void clientViewModel_OnConnectionClosed(object sender, EventArgs e)        {            try            {                Action action = delegate()                {                    clients.Remove(sender as ClientViewModel);                    updateStatus(listenerServer.Status);                    refreshIndexes();                };                dispatcher.Invoke(action, null);            }            catch (Exception ex)            {                MessageBox.Show(ex.Message);            }        }        private void refreshIndexes()        {            ICollectionView view = CollectionViewSource.GetDefaultView(Clients) as ListCollectionView;            view.Refresh();        }        public ICommand DisconnectCommand        {            get            {                if (disconnectCommand == null)                {                    disconnectCommand = new DelegateCommand(Disconnect, canDisconnect);                }                return disconnectCommand;            }        }        private bool canDisconnect()        {            return listening;        }        private void Disconnect()        {            if (listenerServer != null)            {                try                {                    listenerServer.Disconnect();                    listenerServer.OnConnectionAccepted -= new ListenerServer.ConnectionAcceptedDelegate(listenerServer_OnConnectionAccepted);                    listening = false;                }                catch (Exception ex)                {                    MessageBox.Show(ex.Message);                }            }        }        private int getNumberOfClients()        {            int result = 0;            Details detail = ListenerClientsInfo.GetDetails(LOCAL_PORT);            if (detail != null)            {                result = detail.Count;            }            return result;        }

    This ends the MainViewModel code... The compelte MainViewModel Code is:

        public class MainViewModel : ViewModelBase    {        private ObservableCollection<ClientViewModel> clients = new ObservableCollection<ClientViewModel>();        private DelegateCommand exitCommand;        private DelegateCommand listenCommand;        private DelegateCommand disconnectCommand;        private bool listening = false;        private ListenerServer listenerServer;        private const int LOCAL_PORT = 20275;        private string status;        private Dispatcher dispatcher;        #region Constructor        public MainViewModel()        {            Status = "Ready";            dispatcher = Dispatcher.CurrentDispatcher;        }        #endregion        public ObservableCollection<ClientViewModel> Clients        {            get            {                return clients;            }            internal set            {                clients = value;                OnPropertyChanged("Clients");            }        }        public string Status        {            get            {                return status;            }            internal set            {                status = value;                OnPropertyChanged("Status");            }        }        public ICommand ExitCommand        {            get            {                if (exitCommand == null)                {                    exitCommand = new DelegateCommand(Exit);                }                return exitCommand;            }        }        private void Exit()        {            Application.Current.Shutdown();        }        public ICommand ListenCommand        {            get            {                if (listenCommand == null)                {                    listenCommand = new DelegateCommand(Listen, canListen);                }                return listenCommand;            }        }        private bool canListen()        {            return !listening;        }        private void Listen()        {            try            {                PLCFactory.GetChannel(LOCAL_PORT, out listenerServer);                if (listenerServer == null)                    listenerServer = new ListenerServer(LOCAL_PORT, 3, 3000);                listenerServer.OnConnectionAccepted += new ListenerServer.ConnectionAcceptedDelegate(listenerServer_OnConnectionAccepted);                listenerServer.OnConnectionStatusChanged += new ListenerServer.ConnectionStatusChangedDelegate(listenerServer_OnConnectionStatusChanged);                PLCFactory.GetPLC(listenerServer);                listening = true;            }            catch (Exception ex)            {                MessageBox.Show(ex.Message);            }        }        void listenerServer_OnConnectionStatusChanged(EthernetListener.ConnectionStatus connectionStatus)        {            // You can also use a timer to scan the status every, lets say, 1 second.            updateStatus(connectionStatus);        }        private void updateStatus(EthernetListener.ConnectionStatus connectionStatus)        {            string text = "";            switch (connectionStatus)            {                case EthernetListener.ConnectionStatus.Listening:                    text = "Listening, Connected Clients: " + getNumberOfClients().ToString();                    break;                case EthernetListener.ConnectionStatus.Disconnected:                    text = "Disconnected, Connected Clients: " + getNumberOfClients().ToString();                    break;            }            Status = text;        }        void listenerServer_OnConnectionAccepted(PLC plcFromListener)        {            // This event is not raised from the GUIs thread. Therefore we need to invoke it on the            // Main thread using the dispatcher. It is important to create the viewmodels in the GUI threa            // cause we want the dispatcher of the viewmodels to be associated with the right thread.            Action action = delegate()            {                ClientViewModel clientViewModel = new ClientViewModel(plcFromListener);                clientViewModel.OnConnectionClosed += new EventHandler(clientViewModel_OnConnectionClosed);                clients.Add(clientViewModel);                updateStatus(listenerServer.Status);                refreshIndexes();            };            dispatcher.Invoke(action, null);        }        void clientViewModel_OnConnectionClosed(object sender, EventArgs e)        {            try            {                Action action = delegate()                {                    clients.Remove(sender as ClientViewModel);                    updateStatus(listenerServer.Status);                    refreshIndexes();                };                dispatcher.Invoke(action, null);            }            catch (Exception ex)            {                MessageBox.Show(ex.Message);            }        }        private void refreshIndexes()        {            ICollectionView view = CollectionViewSource.GetDefaultView(Clients) as ListCollectionView;            view.Refresh();        }        public ICommand DisconnectCommand        {            get            {                if (disconnectCommand == null)                {                    disconnectCommand = new DelegateCommand(Disconnect, canDisconnect);                }                return disconnectCommand;            }        }        private bool canDisconnect()        {            return listening;        }        private void Disconnect()        {            if (listenerServer != null)            {                try                {                    listenerServer.Disconnect();                    listenerServer.OnConnectionAccepted -= new ListenerServer.ConnectionAcceptedDelegate(listenerServer_OnConnectionAccepted);                    listening = false;                }                catch (Exception ex)                {                    MessageBox.Show(ex.Message);                }            }        }        private int getNumberOfClients()        {            int result = 0;            Details detail = ListenerClientsInfo.GetDetails(LOCAL_PORT);            if (detail != null)            {                result = detail.Count;            }            return result;        }    }

    Now it is time to design the UI :-)The MainView which is a Window that comes with the MVVM Template and it contains Key Binding to the Exit Command (So we can do a Ctr+X).Beside that we also need a Converter so the items in the Listbox will have an Index.

        class PositionConverter : IValueConverter    {        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)        {            ListBoxItem item = value as ListBoxItem;            ListBox view = ItemsControl.ItemsControlFromItemContainer(item) as ListBox;            int index = view.ItemContainerGenerator.IndexFromContainer(item);            return index.ToString();        }        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)        {            throw new Exception("The method or operation is not implemented.");        }    }

    The UI Xaml Is:

    <Window x:Class="Listener_Server_Example.Views.MainView"    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    xmlns:c="clr-namespace:Listener_Server_Example.Commands"    xmlns:local="clr-namespace:Listener_Server_Example"    Title="Main Window" Height="400" Width="800" WindowState="Maximized">        <Window.Resources>        <!-- Allows a KeyBinding to be associated with a command defined in the View Model  -->        <c:CommandReference x:Key="ExitCommandReference" Command="{Binding ExitCommand}" />        <local:PositionConverter x:Key="positionConverter"/>    </Window.Resources>       <Window.InputBindings>        <KeyBinding Key="X" Modifiers="Control" Command="{StaticResource ExitCommandReference}" />     </Window.InputBindings>        <DockPanel>        <Menu DockPanel.Dock="Top">            <MenuItem Header="_File">                <MenuItem Command="{Binding ExitCommand}" Header="E_xit" InputGestureText="Ctrl-X" />            </MenuItem>        </Menu>	        <Grid>            <Grid.RowDefinitions>                <RowDefinition Height="*"/>                <RowDefinition Height="Auto"/>                <RowDefinition Height="Auto"/>            </Grid.RowDefinitions>            <ListView SelectionMode="Single" Grid.Row="0" ItemsSource="{Binding Clients}" x:Name="listView1">                <ListView.View>                    <GridView AllowsColumnReorder="False">                        <GridViewColumn Header="Index" DisplayMemberBinding="{Binding RelativeSource={RelativeSource FindAncestor,                                AncestorType=ListBoxItem}, Converter={StaticResource positionConverter}}" Width="50" />                        <GridViewColumn Header="Client IP" DisplayMemberBinding="{Binding ClientIP}" Width="Auto"/>                        <GridViewColumn Header="PLC Model" DisplayMemberBinding="{Binding PlcModel}" Width="Auto"/>                        <GridViewColumn Header="OS Version" DisplayMemberBinding="{Binding OsVersion}" Width="Auto"/>                        <GridViewColumn Header="PLC Name" DisplayMemberBinding="{Binding PlcName}" Width="Auto"/>                        <GridViewColumn Header="SDW 0" DisplayMemberBinding="{Binding Sdw0}" Width="Auto"/>                        <GridViewColumn Header="Actions" Width="Auto">                            <GridViewColumn.CellTemplate>                                <DataTemplate>                                    <StackPanel Orientation="Horizontal">                                        <Button Content="Disconnect Client" ToolTip="Closes the connection to the PLC" Command="{Binding DisconnectClientCommand}" Margin="2" />                                        <Button Content="Reset PLC" ToolTip="Reset PLC" Command="{Binding ResetPlcCommand}" Margin="2"/>                                        <Button Content="Stop PLC" ToolTip="Stop PLC" Command="{Binding StopPlcCommand}" Margin="2" />                                        <Button Content="Run PLC" ToolTip="Run PLC" Command="{Binding RunPlcCommand}" Margin="2"/>                                    </StackPanel>                                </DataTemplate>                            </GridViewColumn.CellTemplate>                        </GridViewColumn>                                            </GridView>                </ListView.View>            </ListView>                        <StackPanel Orientation="Horizontal" Grid.Row="1">                <Button Content="Listen" Margin="2" ToolTip="Start Listening to port 20275" Command="{Binding ListenCommand}"/>                <Button Content="Disconnect" Margin="2" ToolTip="Stop Listening to port 20275. Connected clients will still be connected." Command="{Binding DisconnectCommand}"/>            </StackPanel>                        <StatusBar Grid.Row="2">                <StackPanel Orientation="Horizontal">                    <TextBlock Text="Status: " Margin="2"/>                    <TextBlock Text="{Binding Status}" Margin="2"/>                </StackPanel>            </StatusBar>        </Grid>    </DockPanel></Window>

    We have a ListView with several columns and a DataTemplate With Disconnect, Reset, etc buttons.I've attached the complete solution.If you find bugs or have suggestions to improve stuff then please don’t hesitate to contact me.Thank you.

    • 0
      entries
    • 0
      comments
    • 1058
      views

    No blog entries yet

  3. A short clip showing the Unitronics V1210 in action with a customers application, checking to see how tender the peas are before harvesting.

  4. TELESTAR (Italian)

    • 1
      entry
    • 0
      comments
    • 3247
      views

    Recent Entries

    unitroni
    Latest Entry

    By unitroni,

    Visit the TELESTAR Blog:

    http://blog.telestar-automation.it/

    logo-telestar-blog.jpg

    Visita il nostro sito ufficiale www.telestar-automation.it

  5. So after a lengthy wait, I finally got the go-ahead to execute my long awaited CanOpen rebuild project in November. I can't go into the details of the machine itself, but I wanted to share a few critical learnings from contending with it during the installation and commissioning.

    1. AC Tech Servo Drives - worked like champions, but I ran into an issue with the OEM limit switches. These were two-wire proxes, and leakage current wrought havoc on my sensitive new drives. This was addressed by installing relays on the switches, which provided a load to prevent false triggering. I also took it a step further and fully isolated the 24VDC circuit of the drives, to protect them from welding noise.

    2. Wago CanOpen - When first powered up, the nodes were throwing an error onto the bus, then working fine thereafter. It turns out the node remembers the IO configuration that was connected to it the first time it was powered on, and if it sees different, generates this error before going to Operational state. Can be corrected by writing "save" to the appropriate register via SDO and CanOpen configuration software.

    3. Numatics CanOpen - After powering up, the nodes would not respond to or generate any PDOs. I could see them in PCAN (software) and everything was right - operational, PDO length reflecting the physical IO attached, and no errors on communication, but also no action from them in response to input being tripped, or output being commanded.

    I grabbed my original test unit and checked it, and made a suprising discovery - the test unit had all 8 bytes of it's PDOs configured. This, regardless of the fact it only had two bytes each worth of IO. I used PCAN to manually configure the PDO on the nodes of the machine, and it all came to life.

    It seems that despite the Numatics node automatically detecting the IO attached, and automatically configuring it's PDO in response, it will not recognize or generate a PDO of any kind if it is, in fact, less than 8 bytes in length.

    Also of note, while the corrected setup is retained, if you get a bus-off error, it will force the node to reconfigure its PDO back to defaults, ie automatic detection again. It has to be corrected manually again to correct.

    4. Motoman - ah, motoman. The CanOpen cards I ordered were bad from the box. Turns out that batch had known firmware issues. After several phone calls and some frantic rushing around, converted the robots to my fallback position of Modbus TCP.

    Lesson - if you are trying to do CanOpen with Motoman, at least in the US, don't. There's zero support for those cards and the cards themselves are questionable at best. Unitronics supports several protocols, just pick a different one for your robots.

    5. IPD Dalsa vision system - nothing but kudos for these guys. I've using the VA31, and it runs terrific. One word of advice, use the hardware trigger input, it's just so much simpler than trying to trigger the image aquisition from modbus. Leave the data to modbus and trip the camera with a relay.

    6. Unitronics - I replaced a Pentium 3 PC running Entivity Steeplechase. It included $16k worth of proprietary hardware, including a Sherlock vision card, two Motion control cards, and a devicenet card, all obsolete, all nearly impossible to obtain.

    I replaced that mess with a Vision 570 PLC, and a combination of CanOpen and Modbus TCP. The PLC cost less than any single component of the original PC based system - if you could even find them anymore.

    That's all for now. Thanks for reading!

    TM

  6. As has been noted elsewhere...

    Protective Lexan Covers are Now Available for All Vision Controllers.

    Even though the Unitronics Vision controllers are very economical, there’s no sense damaging them when you can easily apply a durable protective cover.

    Lexan-Cover-for-Unitronics-Vision-300x179.jpg

    We now offer a self-adhesive Lexan protective film custom cut for each Vision controller from Unitronics. This includes the V130, V350, V280, V290, V530, V560, V570, V1040and V1210.

    These protective covers are resistant to the following chemicals:

    Acetone, Methylene Chloride, Isopropyl Alcohol, Ethyl Acetate, Gasoline, Diesel Fuel, Coffee, Clorox3, Vinegar and more…

    AMPS currently has covers in stock to protect the V130, V350 and V570 controllers.

  7. A lot of my time is spent explaining/verifying/demonstrating. The attached program shows the scan time for a V1040/V1210 and also demonstrates the logic required to do the same for any of the Enhanced family of PLCs.

    SL20 - in V570 gives the scan time in units of 250 ns. In V130 and V350 - in units of 200 ns. In standard Vision models it's not supported.

    Small tip - you can measure execution time of any part of your program via the function Interval. You can find it in VisiLogic > Utilities > Debug. It's described in Help topic On Line Test Mode.

    from: http://forum.unitron...rch__1#entry458

    ScanTime Tester.vlp

  8. Recorded Live:

    Wednesday, June 22nd 2011

    Presented By Anthony Nicolaides

    Applications Engineer - Unitronics Inc.

    This webinar covers the basics of implementing modems with Unitronics controllers.

    WEBINAR - Modems

    <iframe width="960" height="750" src="http://www.youtube.com/embed/t7W7Jt3H3f8?rel=0&hd=1" frameborder="0" allowfullscreen></iframe>

  9. I just programmed a timed filtration system, and was very pleased at how simple the code is to handle a simple on/off timer with selectable day-of-the week:

    timing1.png

    Pretty neat, huh? I coupled that with a simple screen letting the user set start and stop times and select the days to run:

    timing2.png

    and the lion's share of the job was done.

    But there's much more to Unitronics timekeeping than just simple timers. In the forums, there's been some discussion of the difference between RTC and UTC, the use of UTC to do things such as determine long periods of elapsed time, or to store and retrieve date/time values in log files. Let's try to clear up the confusion, shall we?

    First, let's look at the different ways that times can be stored in a Unitronics PLC. For starters, there's RTC (Real Time Clock) - that's a collection of five MI registers, in a specific order:

    Base + 0: Seconds

    Base + 1: Hours and Minutes

    Base + 2: Days and Months

    Base + 3: Year

    Base + 4: Day Of Week

    If you point a function block which uses an RTC value at the special register SI30, you'll get the PLC's built-in Real Time Clock value which should be set for the current date and time of day. You can also point these function blocks at any other group of registers, so that you can manipulate time values which are not the current time, but may instead be values which the user has entered, or values stored in a table, or values which the program has saved for some reason.

    Now, the main reason to use an RTC value is that it is very familiar - there's not much question in our minds about what the "hours" field means, or what "month" it is. There are several problems with this representation, though - while an RTC date is easy for a human to read, it can be a lot of work for a computer to do calculations solely on RTC values. For example, if we want to find out the difference between two RTC dates, we might find that the month has changed and that the later date has a lower day value than the earlier date, and we wind up having to compare and subtract both days and months - but months have different numbers of days depending on the month _and_ the year (that annoying leap year thing!)

    Here's where the UTC value comes in. UTC is a standardized time format which counts the number of seconds elapsed since an "Epoch" value. The Vision series uses an Epoch of Midnight, January 1, 1900 as its starting point, and counts seconds in a 32 bit register moving forward from then. So, for example, 1 AM, January 1, 1900 would have a UTC value of 3600 - 60 minutes of 60 seconds each from the epoch. There are function blocks to convert from RTC register blocks to UTC values, and back again - thus, if you wanted to compare two RTC dates, you would simply convert both values to UTC and subtract the results. That would give you a difference between the dates in seconds, which you could convert to hours, days, or whatever just by dividing by the correct constant (3600 seconds in an hour, 86400 seconds in a day, etc.) It is important to realize that any given time/date value maps one-to-one with a UTC value - thus, 4:14:35 30 March 2004 equals 3,289,608,875 and 3,481,706,253 converts to 12:37:33 1 May 2010. This lets us readily do mathematics and comparisons in the otherwise complicated realm of date handling.

    When would we want to actually do this? How about product packaging - I want to tell an ink-jet printer to print an expiration date which is 28 days from today. Doing this on other PLCs can be a nightmare, requiring a stored table of the days per month plus a special rung to add a day on leap years. On the Unitronics, however, we merely convert the system RTC to a UTC value, add 24192 (28 * 24 * 60 * 60) and convert back from UTC to an RTC at a different location. That RTC value then gets formatted and sent to the printer, and we're done - no worrying about rolling over the month or how many days there are in April.

    Or maybe we want to time a long value - something long enough that trying to count seconds or minutes with an MI register starts producing inaccurate results due to the fact that the program takes time to scan, and every time you add a pulse, you will be off by a few milliseconds. So all you do is convert RTC to UTC in a "Now" DW register, save that value to a "Start Time" DW register at the beginning of your timing interval, and calculate your elapsed time by subtracting "Start Time" from "Now" - the result will be a doubleword integer value of elapsed seconds, accurate to durations of years. (Note that if the user changes the Real Time Clock setting while you're timing, the time result will be wrong.)

    There's more to all of this - things like timer values and date strings in ASCII, but I'll cover some of that in my next post...

  10. En esta ocasión se ha aplicado un V120 de Unitronics con módulos de entrada PT100 y 4-20mA para el control y supervisión de una cámara climática dónde se realiza el curado de muestras de hormigón.

    El V120 ha sido programado para controlar la temperatura y la humedad ambiental mediante lazos PID o todo-nada con histéresis para calentar, enfriar y humidificar la sala. El propio equipo realiza una adquisición de datos durante el tiempo del proceso y los envía a través de una red RS485 a un ordenador central, donde los datos son almacenados en formato Excel para visualizarlos en gráficas e imprimirlos.

    horv120.jpghormigon.0.jpg

    • 1
      entry
    • 1
      comment
    • 4378
      views

    Recent Entries

    Joe Tauser
    Latest Entry

    So now we have the ability to create our own little areas. I will be most interested to see how this new user power evolves.

    • 1
      entry
    • 2
      comments
    • 3770
      views

    Recent Entries

    Thought I would start off my blog with some thought generation!

    What sort of Technologies / Languages are sought after these days? I am basically interested in what any of you more "Techy" Unitronics users prefer as a Software Development language.

    My reason for this is to see what libraries / applications to support Unitronics hardware are missing on Non-Windows or Non .NET environments :)

    Do many of you long for a "Remote Operator" app on Mac or Linux (Maybe even the iPhone). Do many of you wish the .NET Communication Library was available for Java or Python (Or the language of your choice).

    Comment your ideas, thoughts, criticisms... :D

    • 0
      entries
    • 0
      comments
    • 4181
      views

    No blog entries yet

    • 0
      entries
    • 0
      comments
    • 818
      views

    No blog entries yet

×
http:///