Search This Blog

Sunday, January 27, 2013

Setting up Plex

Last December, I setup on Plex media server to create a NextFlix like interface for my mine DVD collection.

Plex lets your stream MP4s to multiple clients at once. In my household, typically there are 2-3 plex clients running at once.  Best part is Plex Media Server is free.

First things is to download Plex Media Center. http://www.plexapp.com/getplex/.

Its available for Windows, OS X, Linux, and some NAS applianaces.

Plex clients are available for computer and mobile devices.  The mobile device clients are not free neither is the Windows 8 client. http://www.plexapp.com/.

Here is my Plex Media server configuration

  • Hosted Plex Media server on a Windows 8 machine Intel 2-CPU single core Xeon 2.5 Ghz, 4GB RAM , 32bit.
  • Using ZyXel NAS325 ARM processor with 4 TB of disk space to host my media files.
  • Mapped shared folder on NAS325 to a drive letter on the Windows 8 machine.
  • Windows 8 machine is connected to my 60in flat screen TV.
  • Also use Plex Media Center as the client on the Windows 8 machine. http://www.plexapp.com/download/plex-media-center.php.
Note: I purchased the Plex client from the Windows 8 store.  It was ~$3.00.  The playback was too slow and choppy.  I downloaded Plex media center and its performance was very nice.

Another cool feature is with a little port forwarding, you can allow access to the personal Plex server over the internet.  I have the paid version of  Plex client for Android $4.99 on my Nexus 7 and Kindle Fire tablets.

Now to complete my TV watching experience on flat screen TV, I installed http://www.unifiedremote.com/ which comprises of a service that is installed on a Windows machine and then a client that is installed on my Nexus 7 tablet.  Now, I have a free convenient mouse /keyboard remote control for Windows!

I also installed Plex Remote which is by far the easiest Plex remote to use.  Allows me to control the Plex media center installed on any of my Windows machines.


I converted my DVDs with

Note: I rip my DVDs on Intel i7 8 core processor 3 GHZ with 8 RAM.  


Total cost for this project is~$400.00.

  • $40.00 for Windows 8 license for old converted PC.
  • $150.00 for Zyxel NAS unit without drives
  • ~$130.00 for 2 - 2TB SATA hard drives - special deal on newegg.
  • $13.00 on Plex clients for Windows 8 and 2 android tablets.
  • ~$40.00 for AnyDVD license.
Great way to recycle an old PC with a decent video card.


Monday, January 21, 2013

No more Crystal Reports

More RDLC Basics

The final piece of the puzzle to solve is how to use multiple report data sets in a report.  

Things to know:
  • You do not need to use sub reports to use more than one data set in  report.
  • Report data set != .net C# Data set.  (This is possibly the dumbest thing about RDLC reports).
  • Report data set  is approximately equivalent to .net data table.
  • Setting up multiple report data sets for a single report is easier than most examples so.
Here is the end point I want:  A report where most of the information comes from one query or view where there is a 1=1 relationship between the various data tables.  The next data set is 1 to many relationship listing multiple items associated a data record.  

My working example: Mine Summary report:
Mine Name, location description, map coordinates, and mineral deposit description comes from one data set.  Minerals found at the mine will come from another data set.

End result will be:


First, I used the Report designer in VS 2010:

Defined the data sets:
From December 1, 2012

And the created the report:
From December 1, 2012

You can add report controls Grids, lists, or text boxes and then assign the values to each.
As you assign the value for each control, you can specify the name of the data set the value is from.
From December 1, 2012

The report data sets map to table adapters I have set up in my DataSet in the project.
From December 1, 2012

I can this approach because I want to have the report be as close to a dumb view as possible.  Any data processing or business logic will be performed in C#. So I create a reference dataset(s) that I can design the report with and stored in the project as reference.

Now, all I have to do is create a couple data tables that match the data sets defined in the report.

Here is the code I use to fetch the data:  1st data table is the 1=1 data of mine name, location and map coordinates.  2nd table is list of mineral occurrences.  A 3rd table is used to concatenate the mineral occurrences to a single text column using a Linq aggregate function.


public static DataSet GetReportDataSet(string reportName, string MDBfile, object rowID)
        {

            try
            {
                EnterProc(MethodBase.GetCurrentMethod());
                var dataset = new DataSet();
                string[] sql = null;
                string[] tablenames = null;

                switch (reportName)
                {
                    case "MineSummary":
                        #region MineSummary
                        sql = new[]{string.Format("SELECT * FROM vwMineSummaryReport WHERE MineID={0}", rowID)
                                      , string.Format("SELECT * FROM vwMineSummary_MineralOcc WHERE ID={0}", rowID)
                                      };

                        tablenames = new[] {"Mine"
                                           ,"MineralOcc"
                                           };
                        #endregion
                        break;
                }

                if (sql != null && sql.Length > 0)
                {
                    FillDataSetFromMDB(MDBfile, sql, tablenames, dataset);
                }

                if (dataset.Tables["MineralOcc"].Rows.Count > 0)
                {
                    var table = new DataTable("Minerals");
                    var col = new DataColumn { ColumnName = "Minerals", DataType = typeof(string) };
                    table.Columns.Add(col);
                    string sMinerals = dataset.Tables["MineralOcc"].Rows.Cast<DataRow>().Aggregate("", (current, row) => current.Length > 0 ? current + string.Format(" , {0} ", row[0]) : string.Format(" {0} ", row[0]));
                    table.Rows.Add(sMinerals.Trim());
                    dataset.Tables.Add((table));
                }


                ExitProc(MethodBase.GetCurrentMethod());
                return dataset;

            }
            catch (Exception ex)
            {
                ErrorLog(ex);
                return null;
            }
        }


The next code block is used to assign the data to report data sets.
(A point a claification:  In my application, the user clicks on a button to print/view/or export a report to PDF, and I use events and arguments to pass the data and the report name from a user control hosting all of the functionality to the parent form which catches the event and calls a method to print / preview or export the report.)

In the code, a report event argument is passed to a method that creates a localreport and assigns multiple report data sets (one per data table in the dataset passed in using the argument.)


static LocalReport PrepareRDLCReport(ReportEventArgs args)
        {
            var rds = args.ReportData != null ? new ReportDataSource("DataSet1", args.ReportData)
                                            : new ReportDataSource("DataSet1", args.ReportDataSet.Tables[0]);
            var report = new LocalReport
            {
                ReportEmbeddedResource = string.Format("DRC.RDLC.{0}.rdlc", args.ReportName)
            };

            report.DataSources.Add(rds);
            if (args.ReportName == "MineSummary")
            {
                report.DataSources.Add(new ReportDataSource("DataSet2", args.ReportDataSet.Tables[1]));
                report.DataSources.Add(new ReportDataSource("DataSet3", args.ReportDataSet.Tables[2]));
            }
            return report;

        }

The final block of code is common code used to print a report.


public static void PrintReport(ReportEventArgs args, Form mdiParent)
        {
            try
            {
                if (mdiParent != null)
                     mdiParent.Cursor = Cursors.WaitCursor;

                DateTime startTime = DateTime.Now;
                switch (args.ReportType)
                {
                    case "CrystalReport":
                        ReportDocument reportobj = PrepareReport(args);
                        if (reportobj != null)            
                                reportobj.PrintToPrinter(1, false, 0, 0);
                        break;
                    case "RDLC":
                        var report = PrepareRDLCReport(args);
                        var reportPrintDoc = new ReportPrintDocument(report)
                            {
                                PrinterSettings = {PrinterName = Properties.Settings.Default.DRC_Printer}
                            };

                        reportPrintDoc.Print();
                        break;
                }
                TimeSpan duration = DateTime.Now - startTime;
                DisplayDuration(mdiParent, duration, string.Format("Print report - {0}", args.ReportName));
            }
            catch (Exception ex)
            {
                DRCCommon.Common.ErrorLog(ex);
            }
        }