csharp/open-rpa/openrpa/OpenRPA/Workflow.cs

Workflow.cs
using Newtonsoft.Json;
using OpenRPA.Interfaces;
using OpenRPA.Interfaces.ensaty;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Threading;

namespace OpenRPA
{
    public clast Workflow : apibase, IWorkflow
    {
        [JsonIgnore]
        private long _current_version = 0;
        public long current_version
        {
            get
            {
                if (_version > _current_version) return _version;
                return _current_version;
            }
            set
            {
                _current_version = value;
            }
        }
        public Workflow()
        {
            Serializable = true;
            IsVisible = true;
        }
        public string queue { get { return GetProperty(); } set { SetProperty(value); } }
        public string Xaml { get { return GetProperty(); } set { _activity = null; SetProperty(value); } }
        public List Parameters { get { return GetProperty(); } set { SetProperty(value); } }
        public bool Serializable { get { return GetProperty(); } set { SetProperty(value); } }
        public string Filename { get { return GetProperty(); } set { SetProperty(value); } }
        [JsonIgnore]
        public string RelativeFilename
        {
            get
            {
                if (Project == null) return Filename;
                if (string.IsNullOrEmpty(Project.Path)) return Filename;
                string lastFolderName = System.IO.Path.GetFileName(Project.Path);
                return System.IO.Path.Combine(lastFolderName, Filename);
            }
        }
        [JsonIgnore]
        public string IDOrRelativeFilename
        {
            get
            {
                if (string.IsNullOrEmpty(RelativeFilename)) return name;
                if (RelativeFilename.Contains("\\")) return RelativeFilename;
                if (Project != null) return Project.name + "\\" + Filename;
                if (!string.IsNullOrEmpty(_ProjectAndName) && _ProjectAndName.Contains("/"))
                {
                    return _ProjectAndName.Substring(0, _ProjectAndName.IndexOf("/") + 1) + RelativeFilename;
                }
                return _id;
                //if (string.IsNullOrEmpty(_id)) return RelativeFilename;
                //return _id;
            }
        }
        private string _ProjectAndName;
        [JsonProperty("projectandname")]
        public string ProjectAndName
        {
            get
            {
                if (Project == null)
                {
                    if (!string.IsNullOrEmpty(_ProjectAndName)) return _ProjectAndName;
                    return name;
                }
                return Project.name + "/" + name;
            }
            set
            {
                _ProjectAndName = value;
            }
        }
        public string FilePath
        {
            get
            {
                if (Project == null) return Filename;
                return System.IO.Path.Combine(Project.Path, Filename);
            }
        }
        public string projectid { get { return GetProperty(); } set { SetProperty(value); } }
        [JsonIgnore]
        public bool IsExpanded { get { return GetProperty(); } set { SetProperty(value); } }
        [JsonIgnore]
        public bool IsSelected { get { return GetProperty(); } set { SetProperty(value); } }
        private string laststate = "unloaded";
        [JsonIgnore]
        public string State
        {
            get
            {
                string state = laststate;
                var instace = Instances;
                if (instace.Count() > 0)
                {
                    var running = instace.Where(x => x.isCompleted == false).ToList();
                    if (running.Count() > 0)
                    {
                        state = "running";
                    }
                    else
                    {
                        laststate = "running";
                        state = instace.OrderByDescending(x => x._modified).First().state;
                    }
                    laststate = state;
                }
                return state;
            }
        }
        public void SetLastState(string State)
        {
            laststate = State;
        }
        [JsonIgnore]
        public string StateImage
        {
            get
            {
                switch (State)
                {
                    case "unloaded": return "/OpenRPA;component/Resources/state/unloaded.png";
                    case "running": return "/OpenRPA;component/Resources/state/Running_green.png";
                    case "aborted": return "/OpenRPA;component/Resources/state/Abort.png";
                    case "failed": return "/OpenRPA;component/Resources/state/failed.png";
                    case "completed": return "/OpenRPA;component/Resources/state/Completed.png";
                    default: return "/OpenRPA;component/Resources/state/unloaded.png";
                }
            }
        }
        public void NotifyUIState()
        {
            NotifyPropertyChanged("State");
            NotifyPropertyChanged("StateImage");
        }
        [JsonIgnore]
        public List Instances
        {
            get
            {
                return WorkflowInstance.Instances.Where(x => (x.WorkflowId == _id && !string.IsNullOrEmpty(_id)) || (x.RelativeFilename == RelativeFilename && string.IsNullOrEmpty(_id))).ToList();
            }
        }
        [JsonIgnore]
        public bool isRunnning
        {
            get
            {
                foreach (var i in WorkflowInstance.Instances.ToList())
                {
                    if (!string.IsNullOrEmpty(_id) && i.WorkflowId == _id)
                    {
                        if (i.state != "completed" && i.state != "aborted" && i.state != "failed")
                        {
                            return true;
                        }
                    }
                }
                return false;
            }
        }
        [JsonIgnore]
        public IProject Project { get; set; }
        public static Workflow FromFile(IProject project, string Filename)
        {
            var result = new Workflow();
            result._type = "workflow";
            result.Project = project;
            result.Filename = System.IO.Path.GetFileName(Filename);
            result.name = System.IO.Path.GetFileNameWithoutExtension(Filename);
            result.Xaml = System.IO.File.ReadAllText(Filename);
            result.Parameters = new List();
            //sresult.Instances = new System.Collections.ObjectModel.ObservableCollection();
            return result;
        }
        public static Workflow Create(IProject Project, string Name)
        {
            Workflow workflow = new Workflow { Project = Project, name = Name, _acl = Project._acl };
            bool isUnique = false; int counter = 1;
            while (!isUnique)
            {
                if (counter == 1)
                {
                    // workflow.FilePath = System.IO.Path.Combine(Project.Path, Name.Replace(" ", "_").Replace(".", "") + ".xaml");
                    workflow.Filename = Name.Replace(" ", "_").Replace(".", "") + ".xaml";
                }
                else
                {
                    workflow.name = Name + counter.ToString();
                    //workflow.FilePath = System.IO.Path.Combine(Project.Path, Name.Replace(" ", "_").Replace(".", "") + counter.ToString() + ".xaml");
                    workflow.Filename = Name.Replace(" ", "_").Replace(".", "") + counter.ToString() + ".xaml";
                }
                if (!System.IO.File.Exists(workflow.FilePath)) isUnique = true;
                counter++;
            }
            workflow._type = "workflow";
            workflow.Parameters = new List();
            //workflow.Instances = new System.Collections.ObjectModel.ObservableCollection();
            workflow.projectid = Project._id;
            return workflow;
        }
        public void SaveFile(string overridepath = null, bool exportImages = false)
        {
            if (string.IsNullOrEmpty(name)) return;
            if (string.IsNullOrEmpty(Xaml)) return;
            if (!Project.Workflows.Contains(this)) Project.Workflows.Add(this);

            var workflowpath = Project.Path;
            if (!string.IsNullOrEmpty(overridepath)) workflowpath = overridepath;
            var workflowfilepath = System.IO.Path.Combine(workflowpath, Filename);
            if (string.IsNullOrEmpty(workflowfilepath))
            {
                Filename = UniqueFilename();
            }
            else
            {
                var guess = name.Replace(" ", "_").Replace(".", "") + ".xaml";
                var newName = UniqueFilename();
                if (guess == newName && Filename != guess)
                {
                    System.IO.File.WriteAllText(System.IO.Path.Combine(workflowpath, guess), Xaml);
                    System.IO.File.Delete(workflowfilepath);
                    Filename = guess;
                    return;
                }
            }
            if (exportImages)
            {
                GenericTools.RunUI(async () =>
                {
                    string beforexaml = Xaml;
                    string xaml = await Views.WFDesigner.LoadImages(beforexaml);
                    //string xaml = Task.Run(() =>
                    //{
                    //    return Views.WFDesigner.LoadImages(beforexaml);
                    //}).Result;
                    System.IO.File.WriteAllText(workflowfilepath, xaml);
                });
                return;
            }
            if (Project.disable_local_caching)
            {
                if (System.IO.File.Exists(workflowfilepath)) System.IO.File.Delete(workflowfilepath);
                return;
            }
            System.IO.File.WriteAllText(workflowfilepath, Xaml);
        }
        public async Task Save(bool UpdateImages)
        {
            try
            {
                SaveFile();
            }
            catch (Exception ex)
            {
                Log.Error(ex.ToString());
            }
            projectid = Project._id;
            if (!global.isConnected) return;
            if (string.IsNullOrEmpty(_id))
            {
                var result = await global.webSocketClient.InsertOne("openrpa", 0, false, this);
                _id = result._id;
                _acl = result._acl;
                _modified = result._modified;
                _modifiedby = result._modifiedby;
                _modifiedbyid = result._modifiedbyid;
            }
            else
            {
                var result = await global.webSocketClient.UpdateOne("openrpa", 0, false, this);
                _acl = result._acl;
                _modified = result._modified;
                _modifiedby = result._modifiedby;
                _modifiedbyid = result._modifiedbyid;
                _version = result._version;
                if (UpdateImages)
                {
                    var files = await global.webSocketClient.Query("files", "{\"metadata.workflow\": \"" + _id + "\"}");
                    foreach (var f in files)
                    {
                        bool equal = f.metadata._acl.SequenceEqual(_acl);
                        if (!equal)
                        {
                            f.metadata._acl = _acl;
                            await global.webSocketClient.UpdateOne("files", 0, false, f);
                        }
                    }
                }
            }
        }
        public async Task Delete()
        {
            try
            {
                if (Project != null && Project.Workflows.Contains(this)) Project.Workflows.Remove(this);
                if (string.IsNullOrEmpty(FilePath)) return;
                if (System.IO.File.Exists(FilePath)) System.IO.File.Delete(FilePath);
                if (!global.isConnected) return;
                if (!string.IsNullOrEmpty(_id))
                {
                    var imagepath = System.IO.Path.Combine(Interfaces.Extensions.ProjectsDirectory, "images");
                    if (!System.IO.Directory.Exists(imagepath)) System.IO.Directory.CreateDirectory(imagepath);
                    var files = await global.webSocketClient.Query("files", "{\"metadata.workflow\": \"" + _id + "\"}");
                    foreach (var f in files)
                    {
                        await global.webSocketClient.DeleteOne("files", f._id);
                        var imagefilepath = System.IO.Path.Combine(imagepath, f._id + ".png");
                        if (System.IO.File.Exists(imagefilepath)) System.IO.File.Delete(imagefilepath);
                    }
                    await global.webSocketClient.DeleteOne("openrpa", this._id);
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex.ToString());
                throw;
            }
        }
        public void RunPendingInstances()
        {
            var statepath = System.IO.Path.Combine(Project.Path, "state");
            if (System.IO.Directory.Exists(statepath))
            {
                var ProjectFiles = System.IO.Directory.EnumerateFiles(statepath, "*.json", System.IO.SearchOption.AllDirectories).OrderBy((x) => x).ToArray();
                if (!global.isConnected)
                {
                    foreach (var f in ProjectFiles)
                    {
                        try
                        {
                            var i = JsonConvert.DeserializeObject(System.IO.File.ReadAllText(f));
                            i.Workflow = this;
                            i.Path = Project.Path;
                            //if (idleOrComplete != null) i.OnIdleOrComplete += idleOrComplete;
                            //if (VisualTracking != null) i.OnVisualTracking += VisualTracking;
                            var exists = WorkflowInstance.Instances.Where(x => x.InstanceId == i.InstanceId).FirstOrDefault();
                            if (exists != null) continue;
                            if (i.state != "failed" && i.state != "aborted" && i.state != "completed")
                            {
                                var _ref = (i as IWorkflowInstance);
                                foreach (var runner in Plugins.runPlugins)
                                {
                                    if (!runner.onWorkflowStarting(ref _ref, true)) throw new Exception("Runner plugin " + runner.Name + " declined running workflow instance");
                                }
                                lock (WorkflowInstance.Instances) WorkflowInstance.Instances.Add(i);
                                i.createApp(Activity);
                                i.Run();
                            }
                        }
                        catch (System.Runtime.DurableInstancing.InstancePersistenceException ex)
                        {
                            Log.Error("RunPendingInstances: " + ex.ToString());
                            try
                            {
                                System.IO.File.Delete(f);
                            }
                            catch (Exception)
                            {
                            }
                        }
                        catch (Exception ex)
                        {
                            Log.Error("RunPendingInstances: " + ex.ToString());
                        }
                    }
                }
            }
        }
        public string UniqueFilename()
        {
            string Filename = ""; string FilePath = "";
            bool isUnique = false; int counter = 1;
            while (!isUnique)
            {
                if (counter == 1)
                {
                    Filename = System.Text.RegularExpressions.Regex.Replace(name, @"[^0-9a-zA-Z]+", "") + ".xaml";
                    FilePath = System.IO.Path.Combine(Project.Path, Filename);
                }
                else
                {
                    Filename = name.Replace(" ", "_").Replace(".", "") + counter.ToString() + ".xaml";
                    FilePath = System.IO.Path.Combine(Project.Path, Filename);
                }
                if (!System.IO.File.Exists(FilePath)) isUnique = true;
                counter++;
            }
            return Filename;
        }
        private System.Activities.Activity _activity = null;
        [Newtonsoft.Json.JsonIgnore]
        public System.Activities.Activity Activity
        {
            get
            {
                try
                {
                    if (string.IsNullOrEmpty(Xaml)) return null;
                    if (_activity != null) return _activity;
                    var activitySettings = new System.Activities.XamlIntegration.ActivityXamlServicesSettings
                    {
                        CompileExpressions = true
                    };
                    var xamlReaderSettings = new System.Xaml.XamlXmlReaderSettings { Localastembly = typeof(Workflow).astembly };
                    var xamlReader = new System.Xaml.XamlXmlReader(new System.IO.StringReader(Xaml), xamlReaderSettings);
                    _activity = System.Activities.XamlIntegration.ActivityXamlServices.Load(xamlReader, activitySettings);
                    return _activity;
                }
                catch (Exception ex)
                {
                    Log.Error(ex.ToString());
                    throw;
                }
            }
        }
        public IWorkflowInstance CreateInstance(Dictionary Parameters, string queuename, string correlationId,
            OpenRPA.Interfaces.idleOrComplete idleOrComplete, OpenRPA.Interfaces.VisualTrackingHandler VisualTracking, string SpanId, string ParentSpanId)
        {
            if (this.Parameters == null) this.Parameters = new List();
            if (this.Parameters.Count == 0)
            {
                ParseParameters();
            }
            var instance = WorkflowInstance.Create(this, Parameters);
            instance.SpanId = SpanId;
            instance.ParentSpanId = ParentSpanId;
            instance.queuename = queuename; instance.correlationId = correlationId;
            if (idleOrComplete != null) instance.OnIdleOrComplete += idleOrComplete;
            if (VisualTracking != null) instance.OnVisualTracking += VisualTracking;
            Instances.Add(instance);
            //instance.Run();
            return instance;
        }
        public System.Collections.ObjectModel.KeyedCollection GetParameters()
        {
            System.Activities.ActivityBuilder ab2;
            using (var stream = new System.IO.MemoryStream(Encoding.UTF8.GetBytes(Xaml)))
            {
                ab2 = System.Xaml.XamlServices.Load(
                    System.Activities.XamlIntegration.ActivityXamlServices.CreateBuilderReader(
                    new System.Xaml.XamlXmlReader(stream))) as System.Activities.ActivityBuilder;
            }
            return ab2.Properties;
        }
        public void ParseParameters()
        {
            Parameters.Clear();
            if (!string.IsNullOrEmpty(Xaml))
            {
                var parameters = GetParameters();
                foreach (var prop in parameters)
                {
                    var par = new workflowparameter() { name = prop.Name };
                    par.type = prop.Type.GenericTypeArguments[0].FullName;
                    string baseTypeName = prop.Type.BaseType.FullName;
                    if (baseTypeName == "System.Activities.InArgument")
                    {
                        par.direction = workflowparameterdirection.@in;
                    }
                    if (baseTypeName == "System.Activities.InOutArgument")
                    {
                        par.direction = workflowparameterdirection.inout;
                    }
                    if (baseTypeName == "System.Activities.OutArgument")
                    {
                        par.direction = workflowparameterdirection.@out;
                    }
                    Parameters.Add(par);
                }
            }
        }
        [JsonIgnore]
        public bool IsVisible { get { return GetProperty(); } set { SetProperty(value); } }
    }

}