using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Xml; using VEPROMS.CSLA.Library; namespace VEPROMS { //C2025-024 Electronic Procedures - Phase 2 (PROMS XML output) //class inherits from normal import/export form //then adds additional functionality #pragma warning disable S101 // Types should be named in PascalCase public partial class dlgExportImportEP : dlgExportImport #pragma warning restore S101 // Types should be named in PascalCase { private readonly AnnotationTypeInfo _AnnotationType; private readonly string multiseparator = ","; private static Regex _ROAccPageTokenPattern = new Regex("[<][^<>-]+-[^<>]+[>]"); public dlgExportImportEP(string mode, FolderInfo folderInfo, frmVEPROMS myFrmVEPROMS, int annotationTypeId, int unitIndex = 0) : base(mode, folderInfo, myFrmVEPROMS, (E_UCFImportOptions)0, unitIndex) { _AnnotationType = AnnotationTypeInfo.Get(annotationTypeId); _ExportBothConvertedandNot = true; DocReplace = new Dictionary(); FormClosed += OnClose; Text = $"{mode} Electronic Procedure ({_AnnotationType.Name}) Dialog for {folderInfo.Name}"; } public dlgExportImportEP(string mode, DocVersionInfo docVersionInfo, frmVEPROMS myFrmVEPROMS, int annotationTypeId, int unitIndex = 0) : base(mode, docVersionInfo, myFrmVEPROMS, (E_UCFImportOptions)0, unitIndex) { _AnnotationType = AnnotationTypeInfo.Get(annotationTypeId); _ExportBothConvertedandNot = true; DocReplace = new Dictionary(); FormClosed += OnClose; Text = $"{mode} Electronic Procedure ({_AnnotationType.Name}) Dialog for {docVersionInfo.Name} of {docVersionInfo.MyFolder.Name}"; } public dlgExportImportEP(string mode, ProcedureInfo procedureInfo, frmVEPROMS myFrmVEPROMS, int annotationTypeId, int unitIndex = 0) : base(mode, procedureInfo, myFrmVEPROMS, (E_UCFImportOptions)0, unitIndex) { _AnnotationType = AnnotationTypeInfo.Get(annotationTypeId); _ExportBothConvertedandNot = true; DocReplace = new Dictionary(); FormClosed += OnClose; Text = $"{mode} Electronic Procedure ({_AnnotationType.Name}) Dialog for {procedureInfo.DisplayNumber}"; } //Overridden function to handle export of EP data protected override void ExportEPAnnotationInfo(XmlElement xe, ItemInfo ii) { if (_UnitIndex > 0) { ii.MyDocVersion.DocVersionConfig.SelectedSlave = _UnitIndex; ii.MyProcedure.MyDocVersion.DocVersionConfig.SelectedSlave = _UnitIndex; } //switch to handle customizations for different formats switch (ii.ActiveFormat.PlantFormat.EPFormatFiles.Find(x => x.AnnotationTypeID == _AnnotationType.TypeID)?.Name) { default: ExportEPAnnotationInfo_Default(xe, ii); break; } ii.MyDocVersion.DocVersionConfig.SelectedSlave = 0; ii.MyProcedure.MyDocVersion.DocVersionConfig.SelectedSlave = 0; } //default export of EP Data private void ExportEPAnnotationInfo_Default(XmlElement xe, ItemInfo ii) { //Add tab text to item string steptab = Volian.Print.Library.PDFReport.BuildStepTab(ii); xe.Attributes.SetNamedItem(AddAttribute(xe.OwnerDocument, "StepTab", steptab)); //Add db sequence to item string dbsequence = dbSeq(ii); xe.Attributes.SetNamedItem(AddAttribute(xe.OwnerDocument, "dbsequence", dbsequence)); //get first transition in item and add it as an xml element if (ii.MyContent.ContentTransitionCount > 0) { TransitionInfo ct = ii.MyContent.ContentTransitions[0]; xe.Attributes.SetNamedItem(AddAttribute(xe.OwnerDocument, "TransitionToItemID", ct.ToID.ToString())); xe.Attributes.SetNamedItem(AddAttribute(xe.OwnerDocument, "TransitionTodbsequence", dbSeq(ct.ToID))); } //export EP annotation details under an EPInfo node if (ii.ItemAnnotations != null) { XmlElement xepinfo = xe.OwnerDocument.CreateElement("EPInfo"); EPFields myEPFields = ii.GetValidEPFields(_AnnotationType.TypeID); ROFSTLookup lookup = ii.MyDocVersion.DocVersionAssociations[0].MyROFst.GetROFSTLookup(ii.MyDocVersion); bool epexportblank = ii.EPexportblank(_AnnotationType.TypeID); //should blank xml elements export? //grab the current RO db so will know location of RO files and default graphics ext. using (RODbInfo myRODB = (RODbInfoList.Get()).FirstOrDefault(x => x.RODbID == ii.MyDocVersion.DocVersionAssociations[0].MyROFst.RODbID)) { //For each annotation in the item that is of the current EP Annotation type foreach (var EPAnnotation in ii.ItemAnnotations.Where(x => x.TypeID == _AnnotationType.TypeID)) { var EPAnnotationConfig = new AnnotationConfig(EPAnnotation.Config); XmlElement xepdetails = xe.OwnerDocument.CreateElement("Details"); //include the annotation ID for reference xepdetails.Attributes.SetNamedItem(AddAttribute(xepdetails.OwnerDocument, "AnnotationID", EPAnnotation.AnnotationID.ToString())); //loop through each EP Field - name the xml elements the EP.name foreach (EPField EP in myEPFields) { string val = EPAnnotationConfig.GetValue("EP", EP.name); if (epexportblank || !string.IsNullOrEmpty(val)) { if (_UnitIndex != 0) { val = DisplayText.ResolveUnitSpecific(ii.MyDocVersion, val); } XmlElement xindivid = xe.OwnerDocument.CreateElement(EP.name); //need to resolve ROs ROSingle, ROMulti, in text //get values switch (EP.type.ToLower()) { case "text": //for text, check if any embedded ROs //if none, set the xml element to the text //otherwise resolve the ROs MatchCollection matches = _ROAccPageTokenPattern.Matches(val); if (matches.Count == 0) { xindivid.InnerText = val; } else { //resolve ROs //text ROs will replace the AccID key in the text //for binary objects like images, //we will keep the AccID in the text and output the binary as a separate child //XML element with the same xml name as the AccID foreach (Match m in matches) { ROFSTLookup.rochild roc = lookup.GetROChildByAccPageID(m.Groups[0].Value); // Exclude replacing Images since are binary - for those, add a sub item if (Enumerable.Range(8, 15).Contains(roc.type)) { xindivid.InnerText = val; XmlElement xroid = AddGraphic(xindivid, m.Groups[0].Value, roc, myRODB, roc.type != 8); xindivid.AppendChild(xroid); } else if (!string.IsNullOrEmpty(roc.value)) { bool convertCaretToDeltaSymbol = (ii.ActiveSection != null) && ii.ActiveSection.ActiveFormat.PlantFormat.FormatData.SectData.ConvertCaretToDelta; string rocvalue = roc.value.Replace("`", "\xB0"); rocvalue = rocvalue.Replace("\xF8", "\xB0"); rocvalue = rocvalue.Replace("\x7F", "\x394"); //delta if (convertCaretToDeltaSymbol) rocvalue = rocvalue.Replace("^", "\x394"); // delta val = val.Replace($"{m.Groups[0].Value}", rocvalue); xindivid.InnerText = val; } } } break; case "rosingle": //Get the output columns from the EPFormatFile //set the "Item" nodes value = to those resolved items //separated by multiseparator XmlElement xindivid_rosingle = xindivid.OwnerDocument.CreateElement("Item"); xindivid_rosingle.Attributes.SetNamedItem(AddAttribute(xindivid_rosingle.OwnerDocument, "ROID", val)); //add values specified in EP input list List ro_single_tmp = EP.getROValuesList(EPAnnotation, val); xindivid_rosingle.InnerText = String.Join(multiseparator, ro_single_tmp.ToArray()); //if image, add location and binary of image // - images are type 8 // but if multiple return values could combine // for example an text (1) + image (8) would be 9 ROFSTLookup.rochild roc_single = lookup.GetRoChild(val); if (Enumerable.Range(8, 15).Contains(roc_single.type)) { XmlElement xroid = AddGraphic(xindivid, val, roc_single, myRODB, roc_single.type != 8); xindivid_rosingle.AppendChild(xroid); } xindivid.AppendChild(xindivid_rosingle); break; case "romulti": //Get the output columns from the EPFormatFile //create an "Item" subnode for each selected RO //set the nodes value = to those resolved items //separated by multiseparator foreach (string ival in val.Split(multiseparator.ToCharArray())) { XmlElement xindivid_romulti = xindivid.OwnerDocument.CreateElement("Item"); xindivid_romulti.Attributes.SetNamedItem(AddAttribute(xindivid_romulti.OwnerDocument, "ROID", ival)); //add values specified in EP input list List ro_multi_tmp = EP.getROValuesList(EPAnnotation, ival); xindivid_romulti.InnerText = String.Join(multiseparator, ro_multi_tmp.ToArray()); //if image, add location and binary of image // - images are type 8 // but if multiple return values could combine // for example an text (1) + image (8) would be 9 ROFSTLookup.rochild roc_multi = lookup.GetRoChild(ival); if (Enumerable.Range(8, 15).Contains(roc_multi.type)) { XmlElement xroid = AddGraphic(xindivid, ival, roc_multi, myRODB, roc_multi.type != 8); xindivid_romulti.AppendChild(xroid); } xindivid.AppendChild(xindivid_romulti); } break; case "tableinput": xindivid.InnerText = val; break; default: xindivid.InnerText = val; break; } xepdetails.AppendChild(xindivid); } } xepinfo.AppendChild(xepdetails); } } xe.AppendChild(xepinfo); } } //return a db sequence string from an Item ID private string dbSeq(int itemID) { using (ItemInfo ii = ItemInfo.Get(itemID)) { return dbSeq(ii); } } //return a db sequence string from an ItemInfo private string dbSeq(ItemInfo ii) => $"{((FolderInfo)ii.MyDocVersion.ActiveParent).Name}:{ii.MyProcedure.DisplayNumber} {ii.MyProcedure.DisplayText}:{ii.DBSequence}"; //For Exporting an RO that is an image //returns the Location and FileName of the RO Image private string GetROImageFileLocation(ROFSTLookup.rochild roc, RODbInfo rodb, bool isMulti) { string rodbpath = rodb.FolderPath; string rocval = roc.value; if (rocval == null) rocval = Array.Find(roc.children, x => x.value.Contains('.')).value; if (rocval == null) return ""; string imgname; if (isMulti) { imgname = rocval.Substring(rocval.IndexOf(' ') + 1, rocval.IndexOf("\r\n") - rocval.IndexOf(' ') - 1); } else { imgname = rocval.Substring(0, rocval.IndexOf('\n')); } int thedot = imgname.LastIndexOf('.'); string fname = imgname; if (thedot == -1 || (thedot != (imgname.Length - 4))) { RODbConfig roDbCfg = new RODbConfig(rodb.Config); fname += string.Format(".{0}", roDbCfg.GetDefaultGraphicExtension()); } string imgfile = Path.Combine(rodbpath, fname); return imgfile; } //For Exporting an RO that is an image //returns an xmlElement // - that is a child to xindivid // - that has a name of Name // - that has a value of the binary representation of the image // - that has an attribute designating the location of the image file private XmlElement AddGraphic(XmlElement xindivid, string Name, ROFSTLookup.rochild roc, RODbInfo rodb, bool isMulti) { Name = Name.Replace("<", "").Replace(">", ""); XmlElement xroid = xindivid.OwnerDocument.CreateElement(Name); string imgfile = GetROImageFileLocation(roc, rodb, isMulti); if (string.IsNullOrEmpty(imgfile)) return xroid; xroid.Attributes.SetNamedItem(AddAttribute(xroid.OwnerDocument, "Location", imgfile)); if (File.Exists(imgfile)) { using (FileStream fsIn = new FileStream(imgfile, FileMode.Open, FileAccess.Read, FileShare.Read)) { // Create an instance of StreamReader that can read characters from the FileStream. using (BinaryReader r = new BinaryReader(fsIn)) xroid.InnerText = Encoding.Default.GetString(r.ReadBytes((int)fsIn.Length)); } } return xroid; } //overridden - used to set the RO location for RO Images that are not in annotations protected override void SetROLocation(ref XmlElement xindivid, ROFSTLookup.rochild roc, RODbInfo rodb, bool isMulti) { string imgfile = GetROImageFileLocation(roc, rodb, isMulti); if (!string.IsNullOrEmpty(imgfile)) xindivid.Attributes.SetNamedItem(AddAttribute(xindivid.OwnerDocument, "Location", imgfile)); } //overridden - used to set specific enhanced doc info protected override void SetEPEnhancedDocLinks(ref XmlElement xe, ItemInfo ii) { EnhancedDocuments eds = ii.GetMyEnhancedDocuments(); if (eds != null && eds.Count == 1) { xe.Attributes.SetNamedItem(AddAttribute(xe.OwnerDocument, "EnhancedDocType", eds[0].Type.ToString())); xe.Attributes.SetNamedItem(AddAttribute(xe.OwnerDocument, "EnhancedDocToItemID", eds[0].ItemID.ToString())); xe.Attributes.SetNamedItem(AddAttribute(xe.OwnerDocument, "EnhancedDocToDbSeq", dbSeq(eds[0].ItemID))); } } //clear objects to release memory private void OnClose(object sender, EventArgs e) { DocReplace.Clear(); DocReplace = null; } } }