private Tuple <string, string, AutoCheckStatus> GetOptimizationInfo(StructureSet structureSet, PlanSetup planSetup) { string value = string.Empty; string details = string.Empty; AutoCheckStatus status = AutoCheckStatus.MANUAL; List <Structure> strList = structureSet.Structures.Where(s => s.Id.ToLower().StartsWith("z_ptv")).ToList(); List <string> lowerPTVObjectiveStructures = new List <string>(); //J Add list for name of all structures that have an lower objective foreach (OptimizationObjective optimizationObjective in planSetup.OptimizationSetup.Objectives) { if (optimizationObjective.GetType() == typeof(OptimizationPointObjective)) { OptimizationPointObjective optimizationPointObjective = (OptimizationPointObjective)optimizationObjective; if (((optimizationPointObjective.Operator.ToString().ToLower() == "lower") || (optimizationPointObjective.Operator.ToString().ToLower() == "upper")) && optimizationPointObjective.StructureId.StartsWith("Z_PTV")) { // Generates a list for with name of all structures that have a lower objective (ie finds the PTVs). lowerPTVObjectiveStructures.Add(optimizationPointObjective.StructureId); } details += (details.Length == 0 ? "Optimization objectives:\r\n " : "\r\n ") + optimizationPointObjective.StructureId + ": " + optimizationPointObjective.Operator.ToString() + ", dose: " + optimizationPointObjective.Dose.Dose.ToString("0.000") + ", volume: " + optimizationPointObjective.Volume.ToString("0.0") + ", priority: " + optimizationPointObjective.Priority.ToString(); } } if (!strList.Any() && !lowerPTVObjectiveStructures.Any()) { value += "Inget optimeringsPTV hittat, verifera"; status = AutoCheckStatus.MANUAL; } else if (strList.Any() && !lowerPTVObjectiveStructures.Any()) { value += "OptimeringsPTV har ritats men ej används i optimering, vänligen verifera"; status = AutoCheckStatus.WARNING; } else if (strList.Any() && lowerPTVObjectiveStructures.Any()) { value += "OptimeringsPTV har ritats och använts optimering, vänligen verifiera"; status = AutoCheckStatus.MANUAL; } // JSR foreach (OptimizationParameter optimizationParameter in planSetup.OptimizationSetup.Parameters) { if (optimizationParameter.GetType() == typeof(OptimizationPointCloudParameter)) { OptimizationPointCloudParameter optimizationPointCloudParameter = (OptimizationPointCloudParameter)optimizationParameter; details += (details.Length == 0 ? string.Empty : "\r\n") + "Point cloud parameter: " + optimizationPointCloudParameter.Structure.Id + "=" + optimizationPointCloudParameter.Structure.DicomType.ToString(); } else if (optimizationParameter.GetType() == typeof(OptimizationNormalTissueParameter)) { OptimizationNormalTissueParameter optimizationNormalTissueParameter = (OptimizationNormalTissueParameter)optimizationParameter; details += (details.Length == 0 ? string.Empty : "\r\n") + "Normal tissue parameter: priority=" + optimizationNormalTissueParameter.Priority.ToString(); } else if (optimizationParameter.GetType() == typeof(OptimizationExcludeStructureParameter)) { OptimizationExcludeStructureParameter optimizationExcludeStructureParameter = (OptimizationExcludeStructureParameter)optimizationParameter; details += (details.Length == 0 ? string.Empty : "\r\n") + "Exclude structure parameter: " + optimizationExcludeStructureParameter.Structure.Id; } } return(new Tuple <string, string, AutoCheckStatus>(value, details, status)); }
public void B() { checklistItems.Add(new ChecklistItem("B. Bolus")); string b1_value = string.Empty; AutoCheckStatus b1_status = AutoCheckStatus.UNKNOWN; if (planSetup.StructureSet != null) { bool tpsBolusExist = false; foreach (Structure structure in planSetup.StructureSet.Structures) { if (string.Compare(structure.DicomType, "BOLUS") == 0) { tpsBolusExist = true; } } if (!tpsBolusExist) { b1_value = "Ej ansatt"; b1_status = AutoCheckStatus.PASS; } else { b1_value = "Bolus har ansatts i TPS"; } } else { b1_value = "StructureSet saknas"; } checklistItems.Add(new ChecklistItem("B1. I dosplaneringssystemet ansatt bolus är med i beräkningen", "Kontrollera att bolus som ansatts i dosplaneringssystemet är med i beräkningen (kopplat till resp. fält)", b1_value, b1_status)); AutoCheckStatus b2_status = AutoCheckStatus.MANUAL; string b2_value = string.Empty; // Check against prescription DataTable bolus = AriaInterface.Query("select PlanSetupSer, PlanSetup.PrescriptionSer, Prescription.PrescriptionSer, BolusFrequency, BolusThickness from PlanSetup, Prescription where PlanSetup.PrescriptionSer = Prescription.PrescriptionSer and PlanSetup.PlanSetupSer = " + planSetupSer.ToString()); if (bolus.Rows.Count > 0) { b2_value += (bolus.Rows[0][3] == DBNull.Value ? string.Empty : (string)bolus.Rows[0][3]); b2_value += (b2_value.Length == 0 ? string.Empty : ", ") + (bolus.Rows[0][4] == DBNull.Value ? string.Empty : (string)bolus.Rows[0][4]); } if (String.IsNullOrWhiteSpace(b2_value)) { b2_value = "Information saknas"; } checklistItems.Add(new ChecklistItem("B2. Ordinationen innehåller information om bolus", "Kontrollera att bolus finns angivet i ordinationen (aktuell tjocklek och bolustyp)\r\n • Notera att uppgifter normalt saknas för de behandlingar där bolus används rutinmässigt (t.ex. 0.5 cm superflabb över ärr för abladerad mam)", b2_value, b2_status)); }
OldChecks() { string u3_value = string.Empty; foreach (Beam beam in planSetup.Beams) { if (!beam.IsSetupField) { if (u3_value != string.Empty) { u3_value += ", "; } u3_value += beam.Id + ": " + Math.Round(beam.Meterset.Value, 1).ToString() + " " + beam.Meterset.Unit.ToString(); } } checklistItems.Add(new ChecklistItem("U3. Jämför MU mellan behandlingsprotokoll och Aria", "Kontrollera att MU stämmer överens mellan Aria/Eclipse och behandlingsprotokoll", u3_value, AutoCheckStatus.MANUAL)); if (checklistType != ChecklistType.EclipseVMAT && checklistType != ChecklistType.MasterPlanIMRT) { checklistItems.Add(new ChecklistItem("U4. Diodvärden finns dokumenterade i protokollet", "Kontrollera att diodvärden finns dokumenterade i protokollet för konventionella planer.\r\nEclipse: Centralaxeln är lämplig som diodpunkt, i annat fall ska manuellt inskrivna diodvärden finnas i protokollet", string.Empty, AutoCheckStatus.MANUAL)); } string g1_value = (image == null ? "-" : image.Comment); AutoCheckStatus g1_status = CheckResult(string.Compare(g1_value, "RT Thorax med gating 3.0 I30s") == 0); checklistItems.Add(new ChecklistItem("G1. CT-studie är korrekt m.a.p. gatingordination", "Kontrollera att det är ritat i korrekt CT-studie m.a.p. gatingordination (anges av läkare under kommentarer under behandlingsordination i behandlingskortet) och Image comment i protokollet", g1_value, g1_status)); string s11_value = string.Empty; AutoCheckStatus s11_status = AutoCheckStatus.PASS; foreach (Beam beam in planSetup.Beams) { string refImageId = string.Empty; DataTable dataTableIDUPosVrt = AriaInterface.Query("select Image.ImageId from Radiation,Image where Image.ImageSer=Radiation.RefImageSer and Radiation.PlanSetupSer=" + planSetupSer.ToString() + " and Radiation.RadiationId='" + beam.Id + "'"); if (dataTableIDUPosVrt.Rows.Count == 1) { refImageId = (string)dataTableIDUPosVrt.Rows[0][0]; } else { s11_status = AutoCheckStatus.FAIL; } s11_value += (s11_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": " + (refImageId.Length == 0 ? "-" : refImageId); } checklistItems.Add(new ChecklistItem("S3. Referensbild/DRR kopplad till alla fält", "Kontrollera att alla fält har en DRR kopplad samt har referensbild kopplad (gul ram runt bildikonen)", s11_value, s11_status)); }
public void X() { checklistItems.Add(new ChecklistItem("X. Slutförande")); //checklistItems.Add(new ChecklistItem("X1. Skriv in antal MU, diodvärden och följande eventuella diodkorrektioner i behandlingskortet", "Skriv in antal MU, diodvärden och följande eventuella diodkorrektioner i behandlingskortet (se ”In vivo-dosimetri”-dokumentet för detaljer):\r\n • Kil (Mimatordioder på Elekta)\r\n • Kort SSD (IBA-dioder)\r\n • Långt SSD (Mimatordioder)", string.Empty, AutoCheckStatus.MANUAL)); string x2_value = string.Empty; AutoCheckStatus x2_status = AutoCheckStatus.MANUAL; if (image != null && image.Series != null) { x2_value = image.Series.ImagingDeviceId; if (string.Compare(image.Series.ImagingDeviceId, "CT_A") == 0 || string.Compare(image.Series.ImagingDeviceId, "CT_B") == 0 || string.Compare(image.Series.ImagingDeviceId, "CT_C") == 0) { if (planSetup.Beams.Count() > 0) { double userY = -image.UserOrigin.y * 0.1; IEnumerable <Beam> txBeams = planSetup.Beams.Where(x => x.IsSetupField == false); //Summarises all ssds for treatment beams, if this is > x*1000 the bool will be true. bool fhaTechnique = txBeams.Select(x => Math.Round(x.SSD)).ToList().Sum() >= (double)txBeams.Count() * 1000; IEnumerable <VVector> uniqueIsos = new List <VVector>(); if (fhaTechnique) { uniqueIsos = GetAllIsocenters(planSetup); } if (uniqueIsos.Count() > 1 && uniqueIsos.Count() <= 2) //This part will give 2 different couch positions based if there are 2 isocenters. { bool warningToggle = false; double firstIsoYPos = double.NaN; foreach (Beam beam in planSetup.Beams) { if (!beam.IsSetupField && !(firstIsoYPos == -(beam.IsocenterPosition.y - image.UserOrigin.y) * 0.1)) { double isoY = -(beam.IsocenterPosition.y - image.UserOrigin.y) * 0.1; if (planSetup.TreatmentOrientation.ToString().IndexOf("Prone") != -1) // Change sign if Orientation is Prone. { isoY *= -1; userY *= -1; } double shiftY = 7.1; double sumY = -isoY - userY + shiftY; x2_value += ", Beräknad britshöjd " + (Double.IsNaN(firstIsoYPos) ? "Iso 1: ": "Iso 2: ") + (-userY).ToString("0.0") + (-isoY >= 0 ? "+" : string.Empty) + (-isoY).ToString("0.0") + "+" + shiftY.ToString("0.0") + " = " + sumY.ToString("0.0") + " cm"; if (sumY < -30 && !warningToggle) { x2_status = AutoCheckStatus.WARNING; } if (Double.IsNaN(firstIsoYPos)) { firstIsoYPos = isoY; } } } } else if (uniqueIsos.Count() > 2) // if more that 2 isos it will promp the user to measure manually. { x2_value += ", Mer än tre iso mät positioner manuellt"; } else { double isoY = -(planSetup.Beams.First().IsocenterPosition.y - image.UserOrigin.y) * 0.1; if (planSetup.TreatmentOrientation.ToString().IndexOf("Prone") != -1) // Change sign if Orientation is Prone. { isoY *= -1; userY *= -1; } double shiftY = 7.1; double sumY = -isoY - userY + shiftY; x2_value += ", Beräknad britshöjd: " + (-userY).ToString("0.0") + (-isoY >= 0 ? "+" : string.Empty) + (-isoY).ToString("0.0") + "+" + shiftY.ToString("0.0") + " = " + sumY.ToString("0.0") + " cm"; if (sumY < -30) { x2_status = AutoCheckStatus.WARNING; } } } } else if (string.Compare(image.Series.ImagingDeviceId, "PET/CT 01") == 0) { x2_value += ", Mät position manuellt"; } else if (string.Compare(image.Series.ImagingDeviceId, "PET/CT 02") == 0) { x2_value += ", Mät position manuellt"; } else if (string.Compare(image.Series.ImagingDeviceId, "PET/CT 03") == 0) { x2_value += ", Mät position manuellt"; } else if (syntheticCT) { x2_value += ", Mät position manuellt"; } } checklistItems.Add(new ChecklistItem("X2. Förväntad britshöjd räknas ut och läggs in i Aria", "Räkna ut förväntad britshöjd och lägg in i Aria (på alla fält, inklusive setup-fält) i modulen Treatment Preparation i rutan för Couch Vrt:\r\n• Eclipse: -DICOM offset Z - isocenter Z + offset cm\r\n• Offset är 7,1 cm för CT_A, CT_B, CT_C\r\n• Observera att vid för prone byter DICOM-koordinaten tecken\r\n• Observera risk för kollision mellan gantry och bord vid Vrt < -30 cm\r\nVid SSD-teknik:\r\n • Ska tjockleken på eventuell vacuumpåse bestämmas genom mätning i CT-bilderna och antecknas under Setup note\r\n • Räkna ut förflyttning från fältet närmast 0° till övriga fält (Isocenterkoordinat för ursprungsfältet minus övriga fälts isocenterkoordinater) och anteckna detta på sida 2 i behandlingsprotokollet. Exempel: Relativ förflyttning från fält 1 till fält 2: ∆Vrt=25,0 cm.\r\n • Skriv följande under Setup note: ”FHA-beh. Ring fysiker vid start.”", x2_value, x2_status)); //checklistItems.Add(new ChecklistItem("X2. Förväntad britshöjd räknas ut och läggs in i Aria", "Räkna ut förväntad britshöjd och lägg in i Aria (på alla fält, inklusive setup-fält) i modulen Treatment Preparation i rutan för Couch Vrt:\r\n• Eclipse: -DICOM offset Z - isocenter Z + offset cm\r\nMasterPlan: -TPRP coordinate Z - isocenter Z + offset cm\r\n• Offset är 7,1 cm för CT_A, CT_B, CT_C och -17,5 för PET/CT 01 (kan dock variera beroende på britshöjd vid PET-undersökningen)\r\n• Observera risk för kollision mellan gantry och bord vid Vrt < -30 cm\r\nVid SSD-teknik:\r\n • Ska tjockleken på eventuell vacuumpåse bestämmas genom mätning i CT-bilderna och antecknas under Setup note\r\n • Räkna ut förflyttning från fältet närmast 0° till övriga fält (Isocenterkoordinat för ursprungsfältet minus övriga fälts isocenterkoordinater) och anteckna detta på sida 2 i behandlingsprotokollet. Exempel: Relativ förflyttning från fält 1 till fält 2: ∆Vrt=25,0 cm.\r\n • Skriv följande under Setup note: ”FHA-beh. Ring fysiker vid start.”", x2_value, x2_status)); // Add elinores corda computation here if (checklistType == ChecklistType.EclipseVMAT && GetVMATCoplanar(planSetup) == false) { // check that setup note for beam "Uppl*gg" contains the string AutoCheckStatus x3_status = AutoCheckStatus.FAIL; string defSetup = "NC-platta används vid behandling pga golvvinkel";//OBS! Icke-coplanar behandling (britsrotation). Använd NC-plattan som förlängning av britsen."; DataTable setupNote = AriaInterface.Query("select SetupNote from Radiation where PlanSetupSer=" + planSetupSer.ToString() + " and UPPER(RadiationId) like 'UPPL%GG'"); foreach (DataRow row in setupNote.Rows) { if (row["SetupNote"].ToString().IndexOf(defSetup) >= 0) { x3_status = AutoCheckStatus.PASS; } } if (x3_status == AutoCheckStatus.FAIL) { Clipboard.SetText(defSetup + "\r\n"); checklistItems.Add(new ChecklistItem("X3. Notera icke coplanar VMAT under Setup note", "Planen i fråga är en icke coplanar VMAT behandling. Säkerställ att en notering om detta finns under planens Setup note. Den exakta formuleringen ska vara: \r\n" + defSetup, string.Empty, defSetup, x3_status)); } else { checklistItems.Add(new ChecklistItem("X3. Notera icke coplanar VMAT under Setup note", "Planen i fråga är en icke coplanar VMAT behandling. Säkerställ att en notering om detta finns under planens Setup note. Den exakta formuleringen ska vara: \r\n" + defSetup, string.Empty, x3_status)); } } if (checklistType == ChecklistType.Eclipse || checklistType == ChecklistType.EclipseGating) { checklistItems.Add(new ChecklistItem("X4. Genomför oberoende MU-kontroll", "Genomför obeorende MU-kontroll via RVP", "", AutoCheckStatus.MANUAL)); } if (checklistType == ChecklistType.EclipseVMAT || checklistType == ChecklistType.EclipseConformal) { AutoCheckStatus x5_status = AutoCheckStatus.MANUAL; string x5_value = string.Empty; string x5_details = string.Empty; //List<string> reqStrings = new List<string>() {"qc","d","4"}; //lägg till kontroll att QC - plan finns och ger annars varning. WORK in progress DataTable qcPlans = AriaInterface.Query("select PlanSetup.PlanSetupId from (select ClinRTPlanSer = RTPlan.RTPlanSer from RTPlan where RTPlan.PlanSetupSer = '" + planSetupSer.ToString() + "') as ClinRTPlan, PlanSetup inner join RTPlan on PlanSetup.PlanSetupSer = RTPlan.PlanSetupSer inner join PlanRelationship on PlanRelationship.RTPlanSer = RTPlan.RTPlanSer where PlanRelationship.RelationshipType = 'VERIFIED_PLAN' and PlanRelationship.RelatedRTPlanSer = ClinRTPlanSer order by PlanSetup.PlanSetupId"); if (qcPlans.Rows.Count > 0) { foreach (DataRow row in qcPlans.Rows) { x5_value += (x5_value.Length == 0 ? "Verifikationsplaner finns: " : ", ") + (string)row["PlanSetupId"]; } if (!(x5_value.IndexOf("QC") > 1 && (x5_value.IndexOf("d") > 1 || x5_value.IndexOf("D") > 1) && x5_value.IndexOf("4") > 1)) { x5_value += " OBS: Inkorrekt namn på verifikationsplan, automatisk export ej möjlig"; x5_status = AutoCheckStatus.WARNING; } } else // SÄtter varning om det inte finns nån QC-plan { x5_status = AutoCheckStatus.FAIL; x5_value = "Det finns ingen QC-plan kopplad till den kliniska planen. Det skall föreligga QC-plan för Delta4."; } checklistItems.Add(new ChecklistItem("X5. QC-planer/QC course sätts till Completed.", "Sätt status på QC coursen till Completed. \nKontroll av befintiliga QC-planer görs. \nNamngivning enligt: QC PX_X Delta4.", x5_value, x5_status)); } if (checklistType == ChecklistType.EclipseGating && image.Comment.IndexOf("DIBH") != -1 || checklistType == ChecklistType.EclipseGating && image.Comment.IndexOf("BH") != -1) { double[] deltaCouch = new double[3]; IEnumerable <VVector> AllIsosPos = GetAllIsocenters(planSetup); if (AllIsosPos.Count() == 1) { Beam beam = planSetup.Beams.Where(b => b.IsSetupField == false).FirstOrDefault(); deltaCouch[0] = -beam.IsocenterPosition.x / 10.0; deltaCouch[1] = -beam.IsocenterPosition.z / 10.0; DataTable CouchPos = AriaInterface.Query("select distinct Slice.CouchVrt from Slice inner join Series on Series.SeriesSer=Slice.SeriesSer where Series.SeriesUID='" + image.Series.UID + "'"); double couchVrt = (double)CouchPos.Rows[0]["CouchVrt"]; deltaCouch[2] = beam.IsocenterPosition.y / 10.0 - couchVrt; checklistItems.Add(new ChecklistItem("X6. Fyll i värden för Delta Couch.", "Fyll i beräknade Delta Couch-värden för planens alla fält.", String.Format("Vrt: {0:N2} cm, Lng: {1:N2} cm, Lat: {2:N2} cm", deltaCouch[2], deltaCouch[1], deltaCouch[0]), String.Format("Delta Couch shift (cm):\r\nVrt:\t{0:N2}\r\nLng:\t{1:N2}\r\nLat:\t{2:N2}", deltaCouch[2], deltaCouch[1], deltaCouch[0]), AutoCheckStatus.MANUAL)); } else { string IsoInfo = string.Empty; DataTable CouchPos = AriaInterface.Query("select distinct Slice.CouchVrt from Slice inner join Series on Series.SeriesSer=Slice.SeriesSer where Series.SeriesUID='" + image.Series.UID + "'"); double couchVrt = (double)CouchPos.Rows[0]["CouchVrt"]; int count = 0; foreach (VVector iso in AllIsosPos) { count += 1; deltaCouch[0] = -iso.x / 10.0; deltaCouch[1] = -iso.z / 10.0; deltaCouch[2] = iso.y / 10 - couchVrt; IsoInfo += "Iso " + count.ToString() + String.Format(": Vrt: {0:N2} cm, Lng: {1:N2} cm, Lat: {2:N2} cm", deltaCouch[2], deltaCouch[1], deltaCouch[0]) + " \r\n\r\n"; } checklistItems.Add(new ChecklistItem("X6. Fyll i värden för Delta Couch.", "Fyll i beräknade Delta Couch-värden för planens alla fält. \r\nDUBBLA ISOCENTER + DIBH! Fysiker importerar till Catalyst", "Planen ifråga har " + (count).ToString() + " isocenter och därmed skall multipla delta couch fyllas i se Detaljer ----->", "Delta Couch shift (cm):\r\n" + IsoInfo, AutoCheckStatus.WARNING)); } //checklistItems.Add(new ChecklistItem("X7. Importera underlag till Catalyst.", "Importera plan och strukturset till Catalyst i enlighet med gällande metodbeskrivning.", String.Empty, AutoCheckStatus.MANUAL)); } checklistItems.Add(new ChecklistItem("X7. Treatment Approved", "Gör planen Treatment Approved. Planen får endast göras Treatment Approved efter att ovanstående kontroller är utförda och Oberoende MU-kontroll eller QC-mätning är godkänd.", string.Empty, AutoCheckStatus.MANUAL)); checklistItems.Add(new ChecklistItem("X8. Task sätts till Done", "Tryck Done när alla kontroller är klara\r\n • Ändra Qty till det antal planer som har kontrollerats\r\n • Om planen har kontrollmätts tycker man Done först när planen både är kontrollerad och kontrollmätt", string.Empty, AutoCheckStatus.MANUAL)); //checklistItems.Add(new ChecklistItem("X5. Signera i rutan Fysiker kontroll", "Genomgången checklista med accepterat resultat bekräftas med signatur i behandlingskortet i rutan Fysiker kontroll.", string.Empty, AutoCheckStatus.MANUAL)); }
public void V() { if (checklistType == ChecklistType.EclipseVMAT || checklistType == ChecklistType.MasterPlanIMRT) { checklistItems.Add(new ChecklistItem("V. VMAT/IMRT")); string v1_value = string.Empty; AutoCheckStatus v1_status = AutoCheckStatus.FAIL; int v1_numberOfWarnings = 0; int v1_numberOfPass = 0; foreach (Beam beam in planSetup.Beams) { if (!beam.IsSetupField) { double collimatorAngle = beam.ControlPoints[0].CollimatorAngle; if (checklistType == ChecklistType.MasterPlanIMRT) { if (collimatorAngle == 2) { v1_numberOfPass++; } else if (collimatorAngle > 2 && collimatorAngle < 358) { v1_numberOfWarnings++; } } else { if (collimatorAngle == 5 || collimatorAngle == 355) { v1_numberOfPass++; } else if (collimatorAngle > 5 && collimatorAngle < 355) { v1_numberOfWarnings++; } } v1_value += (v1_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": " + collimatorAngle.ToString("0.0") + "°"; } } if (v1_numberOfPass == numberOfTreatmentBeams) { v1_status = AutoCheckStatus.PASS; } else if (v1_numberOfPass + v1_numberOfWarnings == numberOfTreatmentBeams) { v1_status = AutoCheckStatus.WARNING; } checklistItems.Add(new ChecklistItem("V1. Kollimatorvinkeln är lämplig", "Kontrollera att kollimatorvinkeln är lämplig\r\n • VMAT: vanligtvis 5° grader resp. 355°, men passar detta ej PTV är andra vinklar ok (dock ej vinklar mellan 355° och 5°)", v1_value, v1_status)); if (checklistType == ChecklistType.EclipseVMAT) { string v2_value = string.Empty; AutoCheckStatus v2_status = AutoCheckStatus.WARNING; int v2_numberOfPass = 0; foreach (Beam beam in planSetup.Beams) { if (!beam.IsSetupField) { double fieldWidth = 0.1 * (beam.ControlPoints[0].JawPositions.X2 - beam.ControlPoints[0].JawPositions.X1); if (fieldWidth <= 15) { v2_numberOfPass++; } v2_value += (v2_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": " + fieldWidth.ToString("0.0") + " cm"; } } if (v2_numberOfPass == numberOfTreatmentBeams) { v2_status = AutoCheckStatus.PASS; } checklistItems.Add(new ChecklistItem("V2. Fältbredden är rimlig ", "Kontrollera att VMAT-fält har en rimlig fältbredd (riktvärde 15 cm, vid större target rekommenderas två arcs och delade fält).", v2_value, v2_status)); string v3_details = string.Empty; if (planSetup.OptimizationSetup != null) { /*foreach (OptimizationObjective optimizationObjective in planSetup.OptimizationSetup.Objectives) * if(optimizationObjective.GetType()==typeof(OptimizationPointObjective)) * { * OptimizationPointObjective optimizationPointObjective = (OptimizationPointObjective)optimizationObjective; * v3_details += (v3_details.Length == 0 ? "Optimization objectives:\r\n " : "\r\n ") + optimizationPointObjective.StructureId + ": " + optimizationPointObjective.Operator.ToString() + ", dose: " + optimizationPointObjective.Dose.Dose.ToString("0.000") + ", volume: " + optimizationPointObjective.Volume.ToString("0.0") + ", priority: " + optimizationPointObjective.Priority.ToString(); * }*/ foreach (OptimizationParameter optimizationParameter in planSetup.OptimizationSetup.Parameters) { if (optimizationParameter.GetType() == typeof(OptimizationPointCloudParameter)) { OptimizationPointCloudParameter optimizationPointCloudParameter = (OptimizationPointCloudParameter)optimizationParameter; v3_details += (v3_details.Length == 0 ? string.Empty : "\r\n") + "Point cloud parameter: " + optimizationPointCloudParameter.Structure.Id + "=" + optimizationPointCloudParameter.Structure.DicomType.ToString(); } else if (optimizationParameter.GetType() == typeof(OptimizationNormalTissueParameter)) { OptimizationNormalTissueParameter optimizationNormalTissueParameter = (OptimizationNormalTissueParameter)optimizationParameter; v3_details += (v3_details.Length == 0 ? string.Empty : "\r\n") + "Normal tissue parameter: priority=" + optimizationNormalTissueParameter.Priority.ToString(); } else if (optimizationParameter.GetType() == typeof(OptimizationExcludeStructureParameter)) { OptimizationExcludeStructureParameter optimizationExcludeStructureParameter = (OptimizationExcludeStructureParameter)optimizationParameter; v3_details += (v3_details.Length == 0 ? string.Empty : "\r\n") + "Exclude structure parameter: " + optimizationExcludeStructureParameter.Structure.Id; } } } checklistItems.Add(new ChecklistItem("V3. Optimeringsbolus är korrekt använt", "Kontrollera att optimeringsbolus har använts korrekt för ytliga target: \r\n Eclipse H&N (VMAT):\r\n • Optimerings-PTV har använts vid optimeringen i de fall då PTV har beskurits med hänsyn till ytterkonturen\r\n • Skillnaderna i maxdos för uncertainty-planerna (±0,4 cm i x, y, resp. z) är <5% relativt orginalplanen. Planerna skapas av dosplaneraren.\r\n • HELP_BODY inkluderar både patientens ytterkontur (BODY) och optimeringsbolus\r\n Eclipse Ani, Recti (VMAT):\r\n • BODY ska inkludera eventuellt optimeringsbolus\r\n Optimeringsbolus i Eclipse (VMAT):\r\n • HU för optimeringsbolus är satt till 0 HU\r\n • Optimeringsbolus är skapat genom 5 mm (H&N) eller 6 mm (Ani, Recti) expansion från det PTV-struktur optimeringen skett på. Boluset ska ej gå innanför patientens hudyta.", string.Empty, v3_details, AutoCheckStatus.MANUAL)); checklistItems.Add(new ChecklistItem("V4. Leveransmönstret är rimligt", "Kontrollera att leveransmönstret är rimligt (att det inte är en stor andel extremt små öppningar och att riskorgan skärmas, samt att alla segment går på ett target)", string.Empty, AutoCheckStatus.MANUAL)); } } }
public void P() { checklistItems.Add(new ChecklistItem("P. Dosplan")); string p1_value = planSetup.ApprovalStatus.ToString(); AutoCheckStatus p1_status = CheckResult(planSetup.ApprovalStatus == PlanSetupApprovalStatus.PlanningApproved); checklistItems.Add(new ChecklistItem("P1. Planen är planning approved", "Kontrollera att planen är Planning Approved i Aria.", p1_value, p1_status)); string p2_value = string.Empty; AutoCheckStatus p2_status = AutoCheckStatus.FAIL; if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Multiple) { p2_status = AutoCheckStatus.WARNING; } int p2_numberOfPass = 0; foreach (Beam beam in planSetup.Beams) { bool fff = (beam.EnergyModeDisplayName.IndexOf("FFF") != -1); if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Varian) { if (fff == false && beam.DoseRate == 600) { p2_numberOfPass++; } else if (fff == true && beam.EnergyModeDisplayName.IndexOf("6") == 0 && beam.DoseRate == 1400) { p2_numberOfPass++; } else if (fff == true && beam.EnergyModeDisplayName.IndexOf("10") == 0 && beam.DoseRate == 2400) { p2_numberOfPass++; } } else if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Elekta) { if (beam.EnergyModeDisplayName.IndexOf("4") == 0 && beam.DoseRate == 250) { p2_numberOfPass++; } else if (beam.EnergyModeDisplayName.IndexOf("6") == 0 && beam.DoseRate == 600) { p2_numberOfPass++; } else if (beam.EnergyModeDisplayName.IndexOf("10") == 0 && beam.DoseRate == 500) { p2_numberOfPass++; } /*{ * if (beam.TreatmentUnit.Id.IndexOf("L05") == -1 && beam.DoseRate == 400) * p2_numberOfPass++; * else if (beam.TreatmentUnit.Id.IndexOf("L05") != -1 && beam.DoseRate == 500) * p2_numberOfPass++; * }*/ } p2_value += (p2_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": " + beam.DoseRate.ToString(); } if (p2_numberOfPass == numberOfBeams) { p2_status = AutoCheckStatus.PASS; } p2_value = reorderBeamParam(p2_value, ","); checklistItems.Add(new ChecklistItem("P2. Dosraten är korrekt", "Kontrollera att dosraten (MU/min) är korrekt:\r\n • Varian: 600 (ej FFF), 1400 (6 MV FFF), 2400 (10 MV FFF)\r\n • Elekta: 250 (4 MV), 600 (6 MV), 500 (10 MV)", p2_value, p2_status)); if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Varian) { string p3_value = string.Empty; AutoCheckStatus p3_status = AutoCheckStatus.FAIL; int p3_numberOfPass = 0; foreach (Beam beam in planSetup.Beams) { if (!beam.IsSetupField) { double treatmentTime = double.NaN; DataTable treatmentTimeTable = AriaInterface.Query("select TreatmentTime from Radiation,ExternalFieldCommon where ExternalFieldCommon.RadiationSer=Radiation.RadiationSer and Radiation.PlanSetupSer=" + planSetupSer.ToString() + " and Radiation.RadiationId='" + beam.Id + "'"); if (treatmentTimeTable.Rows.Count == 1 && treatmentTimeTable.Rows[0][0] != DBNull.Value) { treatmentTime = (double)treatmentTimeTable.Rows[0][0]; double openMU; double wedgedMU; GetMU(beam, out openMU, out wedgedMU); if (beam.EnergyModeDisplayName.IndexOf("FFF") != -1) { if (openMU < 600 && treatmentTime == 0.5) { p3_numberOfPass++; } } else if (checklistType == ChecklistType.EclipseVMAT) { if (openMU <= 400 && treatmentTime == 2) { p3_numberOfPass++; } else if (openMU > 400 && treatmentTime == 3) { p3_numberOfPass++; } } else if (checklistType == ChecklistType.EclipseGating) { if (treatmentTime == 5) { p3_numberOfPass++; } } else { if (openMU <= 500 && wedgedMU == 0 && treatmentTime == 1 || wedgedMU > 0 && wedgedMU <= 300 && treatmentTime == 1) { p3_numberOfPass++; } else if (openMU > 500 && wedgedMU == 0 && treatmentTime == 2 || wedgedMU > 300 && treatmentTime == 2) { p3_numberOfPass++; } } } p3_value += (p3_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": " + (double.IsNaN(treatmentTime) ? "-" : treatmentTime.ToString() + " min"); } } if (p3_numberOfPass == numberOfTreatmentBeams) { p3_status = AutoCheckStatus.PASS; } p3_value = reorderBeamParam(p3_value, ","); checklistItems.Add(new ChecklistItem("P3. Beam on-tiderna är korrekta", "Kontrollera att fälten är tilldelade korrekta beam on-tider:\r\n • 0.5 min för FFF fält med <600 MU\r\n • 1 min för öppna fält med <=500 MU och kilfält med <=300 MU\r\n • 2 min för öppna fält med >500 MU, kilfält med >300 MU, och RapidArc (<=400 MU/arc)\r\n • 3 min för RA (>400 MU/arc)\r\n • 5 min för gating", p3_value, p3_status)); } string p4_value = string.Empty; AutoCheckStatus p4_status = AutoCheckStatus.FAIL; DataTable dataTableUseGated = AriaInterface.Query("select distinct ExternalFieldCommon.MotionCompTechnique from Radiation,ExternalFieldCommon where Radiation.RadiationSer=ExternalFieldCommon.RadiationSer and Radiation.PlanSetupSer=" + planSetupSer.ToString()); if (dataTableUseGated.Rows.Count == 1 && dataTableUseGated.Rows[0][0] != DBNull.Value && string.Compare((string)dataTableUseGated.Rows[0][0], "GATING") == 0) { if (checklistType == ChecklistType.EclipseGating) { p4_status = AutoCheckStatus.PASS; } else { p4_status = AutoCheckStatus.FAIL; } p4_value = "Ikryssad"; } else { if (checklistType == ChecklistType.EclipseGating) { p4_status = AutoCheckStatus.FAIL; } else { p4_status = AutoCheckStatus.PASS; } p4_value = "Ej ikryssad"; } checklistItems.Add(new ChecklistItem("P4. Use Gated är korrekt", "Kontrollera att rutan Use Gated under Plan properties är ikryssad för gatingplaner respektive inte ikryssad för icke-gatingplaner.", p4_value, p4_status)); if (checklistType == ChecklistType.EclipseGating) { string p5_value = string.Empty; AutoCheckStatus p5_status = AutoCheckStatus.FAIL; if (image != null && image.Comment.ToLower().IndexOf("bh") == -1) { int p5_numberOfPass = 0; foreach (Beam beam in planSetup.Beams) { if (!beam.IsSetupField) { double openMU; double wedgedMU; GetMU(beam, out openMU, out wedgedMU); if (wedgedMU == 0) { p5_numberOfPass++; } } } if (p5_numberOfPass == numberOfTreatmentBeams) { p5_status = AutoCheckStatus.PASS; } p5_value = "EIG"; } else { p5_status = AutoCheckStatus.PASS; p5_value = "Breath hold"; } checklistItems.Add(new ChecklistItem("P5. Kilade fält ej förekommande för EIG", "Kontrollera att det inte finns några kilade fält i EIG gating-plan.", p5_value, p5_status)); } string p6_value = string.Empty; string p6_value_detailed = string.Empty; AutoCheckStatus p6_status = AutoCheckStatus.UNKNOWN; DataTable dataTableTreatmentSessions = AriaInterface.Query("select Session.SessionNum,SessionRTPlan.Status from Session,SessionRTPlan,RTPlan where Session.SessionSer=SessionRTPlan.SessionSer and RTPlan.RTPlanSer=SessionRTPlan.RTPlanSer and RTPlan.PlanSetupSer=" + planSetupSer.ToString() + " order by SessionNum"); foreach (DataRow dataRow in dataTableTreatmentSessions.Rows) { int sessionNr = (int)dataRow[0]; string status = (dataRow[1] == DBNull.Value ? "-" : (string)dataRow[1]); p6_value_detailed += (p6_value_detailed.Length == 0 ? string.Empty : "\r\n") + "Session " + sessionNr.ToString() + ": " + status; } p6_value = "# aktiva sessioner: " + dataTableTreatmentSessions.Rows.Count.ToString(); if (fractionation != null && fractionation.NumberOfFractions > 0 && fractionation.NumberOfFractions == dataTableTreatmentSessions.Rows.Count) { p6_status = AutoCheckStatus.PASS; } else { p6_status = AutoCheckStatus.FAIL; } checklistItems.Add(new ChecklistItem("P6. Behandlingssessionerna är aktiva", "Kontrollera att alla behandlingssessioner är aktiva", p6_value, p6_value_detailed, p6_status)); if (checklistType == ChecklistType.Eclipse || checklistType == ChecklistType.EclipseGating || checklistType == ChecklistType.MasterPlan) { string p7_value = string.Empty; AutoCheckStatus p7_status = AutoCheckStatus.FAIL; int p7_numberOfPass = 0; foreach (Beam beam in planSetup.Beams) { if (!beam.IsSetupField) { double diode_value = double.NaN; if (beam.Comment.Length > 0) { NumberFormatInfo numberFormatInfo = new NumberFormatInfo() { NumberDecimalSeparator = ".", NumberGroupSeparator = string.Empty, NegativeInfinitySymbol = "Inf", PositiveInfinitySymbol = "Inf", NaNSymbol = "NaN", NumberDecimalDigits = 0 }; string[] splitString = beam.Comment.ToLower().Split(new string[] { " ", "gy" }, StringSplitOptions.RemoveEmptyEntries); if (splitString.Length == 0) { double.TryParse(beam.Comment.Replace(',', '.'), NumberStyles.Number | NumberStyles.AllowExponent, numberFormatInfo, out diode_value); } else { double.TryParse(splitString[0].Replace(',', '.'), NumberStyles.Number | NumberStyles.AllowExponent, numberFormatInfo, out diode_value); } } double openMU; double wedgedMU; GetMU(beam, out openMU, out wedgedMU); if (double.IsNaN(diode_value) == false) { p7_numberOfPass++; } else { if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Varian && openMU + wedgedMU < 25) { p7_numberOfPass++; } else if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Elekta && openMU < 25 && openMU + wedgedMU < 50) { p7_numberOfPass++; } } p7_value += (p7_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": " + (double.IsNaN(diode_value) ? "-" : diode_value.ToString() + " Gy"); } } if (p7_numberOfPass == numberOfTreatmentBeams) { p7_status = AutoCheckStatus.PASS; } p7_value = reorderBeamParam(p7_value, ","); checklistItems.Add(new ChecklistItem("P7. Diodvärden finns införda under Comments för fälten", "Kontrollera att diodvärden finns införda för fält med >=25 MU alternativt >=50 MU (öppet+kil) för Elekta.", p7_value, p7_status)); } checklistItems.Add(new ChecklistItem("P8. Dosfördelningen är rimlig", "Kontrollera att dosfördelningen är rimlig med avseende på targettäckning och omkringliggande riskorgan", "", AutoCheckStatus.MANUAL)); string p9_value_detailed = string.Empty; string p9_value = String.Empty; bool isSplit = false; AutoCheckStatus p9_status = AutoCheckStatus.UNKNOWN; if (checklistType == ChecklistType.EclipseVMAT) { isSplit = GetIsSplitVMAT(planSetup); } foreach (Beam beam in planSetup.Beams) { if (!beam.IsSetupField) { double openMU; double wedgedMU; GetMU(beam, out openMU, out wedgedMU); p9_value_detailed += beam.Id + ":\r\n"; if (checklistType == ChecklistType.EclipseVMAT) { p9_value_detailed += " Open: " + openMU.ToString("0.0") + " MU\r\n " + beam.MetersetPerGy.ToString("0.0") + " MU/Gy\r\n"; if (beam.MetersetPerGy > 300 && !isSplit) { p9_value += (p9_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": För många MU/Gy"; } else if (beam.MetersetPerGy > 550 && isSplit) { p9_value += (p9_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": För många MU/Gy"; } } else { p9_value_detailed += " Open: " + openMU.ToString("0.0") + ", Wedged: " + wedgedMU.ToString("0.0") + "\r\n"; } p9_value_detailed += " Energi: " + beam.EnergyModeDisplayName + "\r\n\r\n"; if (openMU < 10 && openMU != 0 || wedgedMU < 30 && wedgedMU != 0) { p9_value += (p9_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": För få MU"; } if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Elekta && openMU + wedgedMU > 999) { p9_value += (p9_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": För många MU"; } } } if (p9_value.Length > 0) { if (checklistType == ChecklistType.EclipseVMAT) { p9_status = AutoCheckStatus.WARNING; } else { p9_status = AutoCheckStatus.FAIL; } p9_value = reorderBeamParam(p9_value, ","); } p9_value_detailed = reorderBeamParam(p9_value_detailed, "\r\n\r\n"); checklistItems.Add(new ChecklistItem("P9. Fälten ser rimliga ut vad gäller form, energi, MU och korrektion av artefakter", "Kontrollera att fälten ser rimliga ut vad gäller form, energi, MU och korrektion av artefakter\r\n • Riktlinje för RapidArc är max 300 MU/Gy om bländarna är utanför target under hela varvet (sett ur BEV). Vid delvis skärmat target är denna gräns max 550 MU/Gy.\r\n • Öppna fält ska ha ≥10 MU och fält med fast kil (Elekta) ska ha ≥30 kilade MU.\r\n • För Elekta gäller dessutom att totala antalet MU per fält (öppet + kilat) ej får överstiga 999 MU.", p9_value, p9_value_detailed, p9_status)); if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Elekta) { AutoCheckStatus p10_status = AutoCheckStatus.UNKNOWN; string p10_value = ElektaMLCCheck(planSetup); p10_status = CheckResult(String.Compare(p10_value, "MLC positioner OK.", true) == 0); checklistItems.Add(new ChecklistItem("P10. MLC:n är indragen till X-bländare, och ett/två blad är öppna utanför Y-bländare", "Kontrollera att MLC:n är indragen till X-bländare eller innanför, och att ett helt bladpar är öppet utanför Y-bländare på resp. sida om Y1 resp. Y2 har decimal 0,7, 0,8 eller 0,9.", p10_value, p10_status)); } string p11_value = "Metod: " + planSetup.PlanNormalizationMethod + ", target: " + planSetup.TargetVolumeID + ", prescribed percentage: " + (planSetup.PrescribedPercentage * 100.0).ToString("0.0") + ", värde: " + planSetup.PlanNormalizationValue.ToString("0.0"); AutoCheckStatus p11_status = AutoCheckStatus.MANUAL; double normLimitVMAT = 3.0; if (checklistType == ChecklistType.EclipseVMAT && Math.Abs(planSetup.PlanNormalizationValue - 100) > normLimitVMAT) { p11_status = AutoCheckStatus.FAIL; } checklistItems.Add(new ChecklistItem("P11. Normering är korrekt", "Kontrollera att planen är normerad på korrekt vis \r\n • Normalt till targetvolymens medeldos (om särskilt skäl föreligger kan en punktnormering användas). \r\n • För stereotaktiska lungor i Eclipse normeras dosen till isocenter och ordineras till 75%-isodosen.\r\n • För VMAT ska Plan Normalization Value skall normeringsvärdet vara i intervallet [0.970, 1.030].", p11_value, p11_status)); string p12_value = string.Empty; string p13_value = string.Empty; string p13_value_detailed = string.Empty; AutoCheckStatus p13_status = AutoCheckStatus.WARNING; List <ReferencePoint> referencePoints = new List <ReferencePoint>(); List <double> referencePointDose = new List <double>(); List <double> referencePointTotalDose = new List <double>(); List <int> activeReferencePoints = new List <int>(); // Get dose to reference points in active course foreach (PlanSetup planSetupInCourse in course.PlanSetups) { if (planSetupInCourse.ApprovalStatus != PlanSetupApprovalStatus.UnApproved && planSetupInCourse.ApprovalStatus != PlanSetupApprovalStatus.Rejected) { foreach (Beam beam in planSetupInCourse.Beams) { foreach (FieldReferencePoint fieldReferencePoint in beam.FieldReferencePoints) { int referencePointIndex = -1; for (int refPointNr = 0; refPointNr < referencePoints.Count; refPointNr++) { if (string.Compare(fieldReferencePoint.ReferencePoint.Id, referencePoints[refPointNr].Id) == 0) { referencePointIndex = refPointNr; break; } } if (referencePointIndex == -1) { referencePointIndex = referencePoints.Count; referencePoints.Add(fieldReferencePoint.ReferencePoint); referencePointDose.Add(0.0); referencePointTotalDose.Add(0.0); } referencePointTotalDose[referencePointIndex] += fieldReferencePoint.FieldDose.Dose * (planSetupInCourse.UniqueFractionation == null ? double.NaN : (double)planSetupInCourse.UniqueFractionation.NumberOfFractions); if (planSetupInCourse == planSetup) { referencePointDose[referencePointIndex] += fieldReferencePoint.FieldDose.Dose; if (activeReferencePoints.Contains(referencePointIndex) == false) { activeReferencePoints.Add(referencePointIndex); } } } } } } int p13_numberOfPass = 0; for (int refPointNr = 0; refPointNr < referencePoints.Count; refPointNr++) { p13_value_detailed += (p13_value_detailed.Length == 0 ? string.Empty : "\r\n\r\n") + referencePoints[refPointNr].Id + ":\r\n"; double totalDoseLimit = double.NaN; double dailyDoseLimit = double.NaN; double sessionDoseLimit = double.NaN; DataTable dataTableRefPointLimits = AriaInterface.Query("select distinct RefPoint.RefPointId,RefPoint.TotalDoseLimit,RefPoint.DailyDoseLimit,RefPoint.SessionDoseLimit from PlanSetup,Radiation,RadiationRefPoint,RefPoint where RadiationRefPoint.RefPointSer=RefPoint.RefPointSer and RadiationRefPoint.RadiationSer=Radiation.RadiationSer and PlanSetup.PlanSetupSer=Radiation.PlanSetupSer and PlanSetup.CourseSer=" + courseSer.ToString() + " and RefPoint.RefPointId='" + referencePoints[refPointNr].Id + "'"); if (dataTableRefPointLimits.Rows.Count == 1) { totalDoseLimit = (dataTableRefPointLimits.Rows[0][1] == DBNull.Value ? totalDoseLimit = double.NaN : totalDoseLimit = (double)dataTableRefPointLimits.Rows[0][1]); dailyDoseLimit = (dataTableRefPointLimits.Rows[0][2] == DBNull.Value ? dailyDoseLimit = double.NaN : dailyDoseLimit = (double)dataTableRefPointLimits.Rows[0][2]); sessionDoseLimit = (dataTableRefPointLimits.Rows[0][3] == DBNull.Value ? sessionDoseLimit = double.NaN : sessionDoseLimit = (double)dataTableRefPointLimits.Rows[0][3]); } p13_value_detailed += " Dosbidrag från aktuell plan: " + (fractionation == null ? double.NaN : referencePointDose[refPointNr] * (double)fractionation.NumberOfFractions).ToString("0.000") + " Gy, " + referencePointDose[refPointNr].ToString("0.000") + " Gy/fr " + " (Total dose limit: " + totalDoseLimit.ToString("0.000") + " Gy, daily limit: " + dailyDoseLimit.ToString("0.000") + " Gy, session limit: " + sessionDoseLimit.ToString("0.000") + " Gy)\r\n"; p13_value_detailed += " Totalt dosbidrag från samtliga godkändaplaner: " + referencePointTotalDose[refPointNr].ToString("0.000") + " Gy " + " (Total limit: " + totalDoseLimit.ToString("0.000") + " Gy)"; if (activeReferencePoints.Contains(refPointNr)) // Reference point is present in the active plan { p12_value += (p12_value.Length == 0 ? string.Empty : ", ") + referencePoints[refPointNr].Id + ": " + referencePointDose[refPointNr].ToString("0.000") + " Gy"; p13_value += (p13_value.Length == 0 ? string.Empty : ", ") + referencePoints[refPointNr].Id + ": (T:" + totalDoseLimit.ToString("0.000") + "/D:" + dailyDoseLimit.ToString("0.000") + "/S:" + sessionDoseLimit.ToString("0.000") + " Gy)"; if (Math.Round(referencePointDose[refPointNr], 3) <= Math.Round(dailyDoseLimit, 3) && Math.Round(referencePointDose[refPointNr], 3) <= Math.Round(sessionDoseLimit, 3) && Math.Round(referencePointTotalDose[refPointNr], 3) == Math.Round(totalDoseLimit, 3)) { p13_numberOfPass++; } } } if (activeReferencePoints.Count > 0 && p13_numberOfPass == activeReferencePoints.Count) { p13_status = AutoCheckStatus.UNKNOWN; } checklistItems.Add(new ChecklistItem("P12. Referenspunkternas dosbidrag är korrekta", "Kontrollera att dosbidrag till referenspunkter (dos) är korrekta:\r\n • Varje plan ska ha en punkt (primary reference point) som summerar upp till ordinerad dos för det största PTV som planen primärt behandlar.\r\n • Om flera planer bidrar med dos till samma targetvolymer eller om en plan bidrar med dos till flera targetvolymer ska det finnas referenspunkter utan lokalisation i alla planer som summerar dosen till dessa volymer.\r\n • Referenspunkterna ska inte ha dosbidrag från tidigare behandlingar.", p12_value, AutoCheckStatus.MANUAL)); checklistItems.Add(new ChecklistItem("P13. Referenspunkternas gränser är korrekta", "Kontrollera att referenspunkternas gränser (dos) är korrekta", p13_value, p13_value_detailed, p13_status)); if (checklistType == ChecklistType.Eclipse || checklistType == ChecklistType.EclipseGating) { checklistItems.Add(new ChecklistItem("P14. Skarven är flyttad korrekt för skarvplan", "Skarvplaner: Skarven är flyttad korrekt och fälten är i övrigt likadana\r\n • Bröstbehandlingar med kollimator i 0° för både huvudfält i fossa- och tang.-fält flyttas endast om eventuellt PTV_66 ligger i skarven.", string.Empty, AutoCheckStatus.MANUAL)); } AutoCheckStatus p15_status = AutoCheckStatus.UNKNOWN; string p15_value = string.Empty; int p15_numberOfPass = 0; List <string> machineId = new List <string>(); foreach (Beam beam in planSetup.Beams) { machineId.Add(beam.TreatmentUnit.Id); if (String.Equals(beam.TreatmentUnit.ToString(), planSetup.Beams.First().TreatmentUnit.ToString())) { p15_numberOfPass += 1; } p15_value += (p15_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": " + beam.TreatmentUnit.Id; } if (p15_numberOfPass == numberOfBeams) { p15_status = AutoCheckStatus.PASS; } else { p15_status = AutoCheckStatus.FAIL; } p15_value = reorderBeamParam(p15_value, ","); checklistItems.Add(new ChecklistItem("P15. Konsekvent maskinval", "Kontrollera att samtliga fält är planerade till en och samma behandlingsapparat.", p15_value, p15_status)); AutoCheckStatus p16_status = AutoCheckStatus.UNKNOWN; string p16_value = string.Empty; string p16_value_detailed = "Följande bokningar är aktiva:\r\n"; string treatMachineIdCommon = machineId.ToArray().GroupBy(v => v) .OrderByDescending(g => g.Count()) .First() .Key; p16_value = "Planerat: " + treatMachineIdCommon + (machineId.Distinct().Count() == 1 ? " (enhetligt)" : " (tvetydigt)"); DateTime date = DateTime.Now; List <string> bookedMachineId = new List <string>(); string bookedMachineIdCommon = string.Empty; //DataTable bookings = AriaInterface.Query("select x.MachineId, x.ScheduledStartTime from (SELECT DISTINCT Patient.PatientSer, Patient.PatientId, ScheduledActivity.ActualEndDate,ScheduledActivity.ScheduledActivityCode, ScheduledActivity.ActivityInstanceSer,Machine.MachineId,ScheduledActivity.ScheduledStartTime, ScheduledActivity.ObjectStatus FROM ScheduledActivity,Attendee,Patient,Machine WHERE Patient.PatientId = " + patient.Id.ToString() + " AND ScheduledActivity.ObjectStatus='Active' AND ScheduledActivity.ScheduledActivityCode = 'Open' AND ScheduledActivity.PatientSer=Patient.PatientSer AND Attendee.ActivityInstanceSer=ScheduledActivity.ActivityInstanceSer AND Machine.ResourceSer=Attendee.ResourceSer and ScheduledActivity.ScheduledStartTime > '" + date.ToString("yyyy-MM-dd") + " 00:00:00') as x where PatientId = '" + patient.Id.ToString() + "' ORDER BY ScheduledStartTime"); DataTable bookings = AriaInterface.Query("SELECT DISTINCT Patient.PatientSer, Patient.PatientId, ScheduledActivity.ActualEndDate,ScheduledActivity.ScheduledActivityCode, ScheduledActivity.ActivityInstanceSer,Machine.MachineId,ScheduledActivity.ScheduledStartTime, ScheduledActivity.ObjectStatus FROM ScheduledActivity,Attendee,Patient,Machine WHERE Patient.PatientId = '" + patient.Id.ToString() + "' AND ScheduledActivity.ObjectStatus='Active' AND ScheduledActivity.ScheduledActivityCode = 'Open' AND ScheduledActivity.PatientSer=Patient.PatientSer AND Attendee.ActivityInstanceSer=ScheduledActivity.ActivityInstanceSer AND Attendee.ObjectStatus='Active' AND Machine.MachineType = 'RadiationDevice' AND Machine.ResourceSer=Attendee.ResourceSer and ScheduledActivity.ScheduledStartTime > '" + date.ToString("yyyy-MM-dd") + " 00:00:00' ORDER BY ScheduledStartTime"); if (bookings.Rows.Count > 0) { foreach (DataRow row in bookings.Rows) { bookedMachineId.Add((string)row["MachineId"]); p16_value_detailed += (string)row["ScheduledStartTime"].ToString() + ": " + (string)row["MachineId"] + "\r\n"; } bookedMachineIdCommon = bookedMachineId.ToArray().GroupBy(v => v) .OrderByDescending(g => g.Count()) .First() .Key; p16_value += ", Bokat: " + bookedMachineIdCommon + (bookedMachineId.Distinct().Count() == 1 ? " (enhetligt)" : " (tvetydigt)"); if (String.Equals(treatMachineIdCommon, bookedMachineIdCommon, StringComparison.OrdinalIgnoreCase) && bookedMachineId.Distinct().Count() == 1 && machineId.Distinct().Count() == 1) { p16_status = AutoCheckStatus.PASS; } else if (String.Equals(treatMachineIdCommon, bookedMachineIdCommon, StringComparison.OrdinalIgnoreCase)) { p16_status = AutoCheckStatus.MANUAL; } else { p16_status = AutoCheckStatus.FAIL; } } else { p16_value += ", Bokat: -"; p16_status = AutoCheckStatus.WARNING; } checklistItems.Add(new ChecklistItem("P16. Konsekvens mellan planerad och bokad behandlingsapparat.", "Kontrollera att patienten är bokad till den behandlingsapparat som planen är planerad för.", p16_value, p16_value_detailed, p16_status)); }
public void S() { checklistItems.Add(new ChecklistItem("S. Setup")); if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Varian) { string s1_value = string.Empty; AutoCheckStatus s1_status = AutoCheckStatus.FAIL; int s1_numberOfPass = 0; int s1_numberOfWarnings = 0; foreach (Beam beam in planSetup.Beams) { double iduPosVrt = double.NaN; DataTable dataTableIDUPosVrt = AriaInterface.Query("select IDUPosVrt from Radiation,ExternalFieldCommon where ExternalFieldCommon.RadiationSer=Radiation.RadiationSer and Radiation.PlanSetupSer=" + planSetupSer.ToString() + " and Radiation.RadiationId='" + beam.Id + "'"); if (dataTableIDUPosVrt.Rows.Count == 1 && dataTableIDUPosVrt.Rows[0][0] != DBNull.Value && !Operators.LikeString(beam.Id.ToLower(), "Uppl*gg".ToLower(), CompareMethod.Text)) { iduPosVrt = (double)dataTableIDUPosVrt.Rows[0][0]; if (iduPosVrt == -50) { s1_numberOfPass++; } else { s1_numberOfWarnings++; } } else if (Operators.LikeString(beam.Id.ToLower(), "Uppl*gg".ToLower(), CompareMethod.Text)) // Field with Id Upplägg may have any ImageVrt Position, even undefined. { s1_numberOfPass++; } s1_value += (s1_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": " + (double.IsNaN(iduPosVrt) ? "-" : ((double)iduPosVrt).ToString("0.0") + " cm"); } if (s1_numberOfPass == numberOfBeams) { s1_status = AutoCheckStatus.PASS; } else if (s1_numberOfPass + s1_numberOfWarnings == numberOfBeams) { s1_status = AutoCheckStatus.WARNING; } s1_value = reorderBeamParam(s1_value, ","); checklistItems.Add(new ChecklistItem("S1. Bildplattans vertikala position är -50 cm om inte särskilda skäl föreligger", "Kontrollera att bildplattans vertikala position är -50 cm om inte särskilda skäl föreligger", s1_value, s1_status)); } if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Elekta) { string s2_value = string.Empty; string s2_value_detail = String.Empty; AutoCheckStatus s2_status = AutoCheckStatus.PASS; //List<double> s2_gantryAngles = new List<double>(); Dictionary <string, double> s2_gantryAngles = new Dictionary <string, double>(); foreach (Beam beam in planSetup.Beams) { if (!beam.IsSetupField) { s2_gantryAngles.Add(beam.Id, beam.ControlPoints[0].GantryAngle); s2_value += (s2_value.Length == 0 ? "Sida: " + treatmentSide.ToString() + ", " : ", ") + beam.Id + ": " + beam.ControlPoints[0].GantryAngle.ToString("0.0"); } } if (treatmentSide == TreatmentSide.PlusX) { bool fieldsOnOppositeSide = false; foreach (KeyValuePair <string, double> entry in s2_gantryAngles) { if (entry.Value > 185 && entry.Value <= 290) { fieldsOnOppositeSide = true; } } if (fieldsOnOppositeSide == false) { foreach (KeyValuePair <string, double> entry in s2_gantryAngles) { if (entry.Value <= 270 && entry.Value > 180 && checklistType != ChecklistType.EclipseVMAT) //added to ignore vmat to avoid error for elekta vmat { s2_status = AutoCheckStatus.WARNING; s2_value_detail += (s2_value_detail.Length == 0 ? String.Empty : "\r\n") + entry.Key + ": " + entry.Value.ToString("0.0"); } } } } else { bool fieldsOnOppositeSide = false; foreach (KeyValuePair <string, double> entry in s2_gantryAngles) { if (entry.Value >= 70 && entry.Value < 175) { fieldsOnOppositeSide = true; } } if (fieldsOnOppositeSide == false) { foreach (KeyValuePair <string, double> entry in s2_gantryAngles) { if (entry.Value >= 90 && entry.Value <= 180 && checklistType != ChecklistType.EclipseVMAT) //added to ignore vmat avoid error for elekta vmat { s2_status = AutoCheckStatus.WARNING; s2_value_detail += (s2_value_detail.Length == 0 ? String.Empty : "\r\n") + entry.Key + ": " + entry.Value.ToString("0.0"); } } } } s2_value = reorderBeamParam(s2_value, ","); if (s2_value_detail.Length > 0) { s2_value_detail = reorderBeamParam(s2_value_detail, "\r\n"); s2_value_detail = "Gantryt kommer att rotera ofördelaktigt för att nå följande fält\r\n" + s2_value_detail; checklistItems.Add(new ChecklistItem("S2. Fördelaktiga gantryvinklar har valts.", "Kontrollera att fördelaktiga gantryvinklar har valts med avsseende på rotationsriktning för fält nära 180°. Om det finns fält som går över långt på motstående sida (70° resp. 290°) ges inga rekommendationer. I annat fall gäller rekommendationen:\r\n • Vänstersidiga behandlingar: Fält med 180<gantryvinkel<=270 undanbedes\r\n • Högersidiga behandlingar: Fält med 90<=gantryvinkel<=180 undanbedes\r\n • Notera att det omvända förhållandet mellan behandlingssida och fält gäller om patienten är orienterad Feet First (fötterna mot gantryt)", s2_value, s2_value_detail, s2_status)); } else { checklistItems.Add(new ChecklistItem("S2. Fördelaktiga gantryvinklar har valts.", "Kontrollera att fördelaktiga gantryvinklar har valts med avsseende på rotationsriktning för fält nära 180°. Om det finns fält som går över långt på motstående sida (70° resp. 290°) ges inga rekommendationer. I annat fall gäller rekommendationen:\r\n • Vänstersidiga behandlingar: Fält med 180<gantryvinkel<=270 undanbedes\r\n • Högersidiga behandlingar: Fält med 90<=gantryvinkel<=180 undanbedes\r\n • Notera att det omvända förhållandet mellan behandlingssida och fält gäller om patienten är orienterad Feet First (fötterna mot gantryt)", s2_value, s2_status)); } } if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Varian) { string s2_value = string.Empty; AutoCheckStatus s2_status = AutoCheckStatus.FAIL; int s2_numberOfPass = 0; List <double> s2_gantryAngles = new List <double>(); List <bool> s2_gantryAnglesExtended = new List <bool>(); foreach (Beam beam in planSetup.Beams) { if (!beam.IsSetupField) { DataTable dataTableExtended = AriaInterface.Query("select GantryRtnExt from ExternalField,Radiation where GantryRtnExt='EN' and ExternalField.RadiationSer=Radiation.RadiationSer and Radiation.PlanSetupSer=" + planSetupSer.ToString() + " and Radiation.RadiationId='" + beam.Id + "'"); bool extended = (dataTableExtended.Rows.Count == 1); s2_gantryAnglesExtended.Add(extended); s2_gantryAngles.Add(beam.ControlPoints[0].GantryAngle); s2_value += (s2_value.Length == 0 ? "Sida: " + treatmentSide.ToString() + ", " : ", ") + beam.Id + ": " + beam.ControlPoints[0].GantryAngle.ToString("0.0") + (extended ? "E" : string.Empty); } } if (checklistType == ChecklistType.EclipseVMAT) { foreach (bool extended in s2_gantryAnglesExtended) { if (!extended) { s2_numberOfPass++; } } } else if (treatmentSide == TreatmentSide.PlusX) { bool fieldsOnOppositeSide = false; foreach (double angle in s2_gantryAngles) { if (angle > 185 && angle <= 290) { fieldsOnOppositeSide = true; } } for (int beamNr = 0; beamNr < s2_gantryAngles.Count; beamNr++) { if (fieldsOnOppositeSide) { if (s2_gantryAnglesExtended[beamNr] == false) { s2_numberOfPass++; } } else { if (s2_gantryAngles[beamNr] > 180 && s2_gantryAngles[beamNr] <= 185) { if (s2_gantryAnglesExtended[beamNr] == true) { s2_numberOfPass++; } } else { if (s2_gantryAnglesExtended[beamNr] == false) { s2_numberOfPass++; } } } } } else { bool fieldsOnOppositeSide = false; foreach (double angle in s2_gantryAngles) { if (angle >= 70 && angle < 175) { fieldsOnOppositeSide = true; } } for (int beamNr = 0; beamNr < s2_gantryAngles.Count; beamNr++) { if (fieldsOnOppositeSide) { if (s2_gantryAnglesExtended[beamNr] == false) { s2_numberOfPass++; } } else { if (s2_gantryAngles[beamNr] >= 175 && s2_gantryAngles[beamNr] <= 180) { if (s2_gantryAnglesExtended[beamNr] == true) { s2_numberOfPass++; } } else { if (s2_gantryAnglesExtended[beamNr] == false) { s2_numberOfPass++; } } } } } if (s2_numberOfPass == numberOfTreatmentBeams) { s2_status = AutoCheckStatus.PASS; } s2_value = reorderBeamParam(s2_value, ","); checklistItems.Add(new ChecklistItem("S2. Extended har valts korrekt på behandlingsfält", "Kontrollera att Extended har valts korrekt på behandlingsfält. Om det finns fält som går över långt på motstående sida (70° resp. 290°) ska inga fält ha Extended. I annat fall gäller:\r\n • Vänstersidiga behandlingar: Fält med 180<gantryvinkel<=185 ska ha Extended\r\n • Högersidiga behandlingar: Fält med 175<=gantryvinkel<=180 ska ha Extended\r\n • Övriga fält ska ej ha Extended\r\n • Notera att det omvända förhållandet mellan behandlingssida och fält som ska ha Extended gäller om patienten är orienterad Feet First (fötterna mot gantryt)", s2_value, s2_status)); } string s3_value = string.Empty; AutoCheckStatus s3_status = AutoCheckStatus.UNKNOWN; List <double> s3_setupFieldAngles = new List <double>(); List <double> s4_setupFieldCouchAngles = new List <double>(); List <double> s4_FieldCouchAngles = new List <double>(); List <string> s3_beamIds = new List <string>(); foreach (Beam beam in planSetup.Beams) { if (beam.IsSetupField && !Operators.LikeString(beam.Id.ToLower(), "Uppl*gg".ToLower(), CompareMethod.Text)) { s3_setupFieldAngles.Add(beam.ControlPoints[0].GantryAngle); s4_setupFieldCouchAngles.Add(beam.ControlPoints[0].PatientSupportAngle); s3_beamIds.Add(beam.Id.ToLower()); s3_value += (s3_value.Length == 0 ? "Sida: " + treatmentSide.ToString() + ", " : ", ") + beam.Id + ": " + beam.ControlPoints[0].GantryAngle.ToString("0.0"); } else if (!beam.IsSetupField) { s4_FieldCouchAngles.Add(beam.ControlPoints[0].PatientSupportAngle); } } int s3_cbctIndex = s3_beamIds.IndexOf("cbct"); if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Varian) { if (s3_setupFieldAngles.Count == 0) { s3_value = "Setupfält saknas i planen"; } else if (s3_setupFieldAngles.Count == 1 && s3_cbctIndex == 0) { s3_status = AutoCheckStatus.PASS; } else if (s3_setupFieldAngles.Count == 3) { // Ta bort CBCT från fortsatta kontrollen if (s3_cbctIndex != -1) { s3_beamIds.RemoveAt(s3_cbctIndex); s3_setupFieldAngles.RemoveAt(s3_cbctIndex); } } if (s3_setupFieldAngles.Count == 2) { if (treatmentSide == TreatmentSide.PlusX) // Vänstersidig { if (s3_setupFieldAngles.IndexOf(0) != -1 && s3_setupFieldAngles.IndexOf(270) != -1) { s3_status = AutoCheckStatus.PASS; } else { s3_status = AutoCheckStatus.FAIL; } } else // Högersidig { if (s3_setupFieldAngles.IndexOf(180) != -1 && s3_setupFieldAngles.IndexOf(270) != -1) { s3_status = AutoCheckStatus.PASS; } else { s3_status = AutoCheckStatus.FAIL; } } } } else { s3_status = AutoCheckStatus.MANUAL; // If not Varian, then do manual check of setup angles. } s3_value = reorderBeamParam(s3_value, ","); checklistItems.Add(new ChecklistItem("S3. Gantryvinklar för setupfälten är korrekta", "Kontrollera att setupfältens gantryvinklar är korrekta (patientgeometrin avgör vinklar)\r\n • Standard: 270° respektive 0°\r\n• • Högersidiga behandlingar: 180° respektive 270°", s3_value, s3_status)); string s4_value = string.Empty; AutoCheckStatus s4_status = AutoCheckStatus.MANUAL; if (s4_setupFieldCouchAngles.Count == 0) { s4_value = "Setupfält saknas i planen"; } else { for (int i = 0; i < s4_setupFieldCouchAngles.Count; i++) { if (s4_setupFieldCouchAngles[i] != 0) { s4_value += (s4_value.Length == 0 ? "Setupfält har följande vinklar: " : ", ") + s3_beamIds[i] + ": " + s4_setupFieldCouchAngles[i].ToString("0.0"); s4_status = AutoCheckStatus.WARNING; } } s4_value = reorderBeamParam(s4_value, ","); //Gives manual if sum of all setupfields are 0 but tx fields are different. Gives auto OK if ALL fields have tableangle 0; if (s4_setupFieldCouchAngles.Sum() == 0 && s4_FieldCouchAngles.Sum() != 0) { s4_status = AutoCheckStatus.MANUAL; s4_value = "Alla setupfält har golvvinkel 0°, behandlingsfält har ej golvvinkel 0°"; } if (s4_setupFieldCouchAngles.Sum() == 0 && s4_FieldCouchAngles.Sum() == 0) { s4_status = AutoCheckStatus.PASS; s4_value = "Alla setupfält och behandlingsfält har golvvinkel 0°"; } } checklistItems.Add(new ChecklistItem("S4. Golvvinklar för setupfälten är korrekta", "Kontrollera att setupfältens golvvinklar är korrekta. Om ej särskilda anledningar föreligger ska golvvinkel för samtliga setupfält vara 0°", s4_value, s4_status)); string s5_value; AutoCheckStatus s5_status = AutoCheckStatus.FAIL; string s5_value_detailed = string.Empty; int s5_numberOfPass = 0; bool s5_isocenterCouldNotBeDetermined = false; VVector s5_isocenterPosition = new VVector(double.NaN, double.NaN, double.NaN); List <VVector> allIsoPos = new List <VVector>(); foreach (Beam beam in planSetup.Beams) { double allowedDiff = 0.0; // the allowed difference between isocenters in mm if (double.IsNaN(s5_isocenterPosition.x) && double.IsNaN(s5_isocenterPosition.y) && double.IsNaN(s5_isocenterPosition.z)) { s5_isocenterPosition = beam.IsocenterPosition; allIsoPos.Add(s5_isocenterPosition); if (double.IsNaN(beam.IsocenterPosition.x) == false && double.IsNaN(beam.IsocenterPosition.y) == false && double.IsNaN(beam.IsocenterPosition.z) == false) { s5_numberOfPass++; } else { s5_isocenterCouldNotBeDetermined = true; } } //else if (Math.Round(s5_isocenterPosition.x, 1) == Math.Round(beam.IsocenterPosition.x, 1) && Math.Round(s5_isocenterPosition.y, 1) == Math.Round(beam.IsocenterPosition.y, 1) && Math.Round(s5_isocenterPosition.z, 1) == Math.Round(beam.IsocenterPosition.z, 1)) else if (Math.Abs(s5_isocenterPosition.x - beam.IsocenterPosition.x) <= allowedDiff && Math.Abs(s5_isocenterPosition.y - beam.IsocenterPosition.y) <= allowedDiff && Math.Abs(s5_isocenterPosition.z - beam.IsocenterPosition.z) <= allowedDiff) { s5_numberOfPass++; allIsoPos.Add(beam.IsocenterPosition); } else { allIsoPos.Add(beam.IsocenterPosition); } } if (s5_numberOfPass == numberOfBeams)//numberOfTreatmentBeams) { s5_value = "Samma isocenter"; s5_status = AutoCheckStatus.PASS; } else if (s5_isocenterCouldNotBeDetermined) { s5_value = "Isocenterposition kunde ej bestämmas"; s5_status = AutoCheckStatus.WARNING; } else { s5_value = "Olika isocenter mellan fälten"; // Generates value-string with information of the two isocenters. int i = 0; string nameIso1 = "Iso 1: "; string nameIso2 = "Iso 2: "; VVector storedIso1 = new VVector(double.NaN, double.NaN, double.NaN); VVector storedIso2 = new VVector(double.NaN, double.NaN, double.NaN); if (allIsoPos.Distinct().Count() == 2) { foreach (VVector v in allIsoPos) { if (v.x == s5_isocenterPosition.x && v.y == s5_isocenterPosition.y && v.z == s5_isocenterPosition.z) { nameIso1 += planSetup.Beams.ElementAt(i).Id + ", "; storedIso1 = planSetup.Beams.ElementAt(i).IsocenterPosition - image.UserOrigin; } else { nameIso2 += planSetup.Beams.ElementAt(i).Id + ", "; storedIso2 = planSetup.Beams.ElementAt(i).IsocenterPosition - image.UserOrigin; } s5_value_detailed += "Fält " + planSetup.Beams.ElementAt(i).Id + ": \r\nX: " + 0.1 * (planSetup.Beams.ElementAt(i).IsocenterPosition.x - image.UserOrigin.x) + " , Y: " + 0.1 * (planSetup.Beams.ElementAt(i).IsocenterPosition.z - image.UserOrigin.z) + " , Z: " + -0.1 * (planSetup.Beams.ElementAt(i).IsocenterPosition.y - image.UserOrigin.y) + ". \r\n"; i++; } s5_value = "Olika isocenter mellan fälten: " + nameIso1 + " X:" + 0.1 * storedIso1.x + " Y:" + 0.1 * storedIso1.z + " Z:" + -0.1 * storedIso1.y + " \n " + nameIso2 + " X:" + 0.1 * storedIso2.x + " Y:" + 0.1 * storedIso2.z + " Z:" + -0.1 * storedIso2.y; } else { int j = 0; s5_value = "Minst tre isocenter mellan fälten. Vänligen korrigera: "; s5_value_detailed = "Olika isocenter mellan fälten. Vänligen korrigera: \r\n"; foreach (VVector v in allIsoPos) { s5_value_detailed += "Fält " + planSetup.Beams.ElementAt(j).Id + ": \r\nX: " + 0.1 * (planSetup.Beams.ElementAt(j).IsocenterPosition.x - image.UserOrigin.x) + " , Y: " + 0.1 * (planSetup.Beams.ElementAt(j).IsocenterPosition.z - image.UserOrigin.z) + " , Z: " + -0.1 * (planSetup.Beams.ElementAt(j).IsocenterPosition.y - image.UserOrigin.y) + ". \r\n"; j++; } } s5_status = AutoCheckStatus.WARNING; } checklistItems.Add(new ChecklistItem("S5. Alla fält har samma isocenter vid isocentrisk teknik", "Kontrollera att samtliga fälts (inklusive setup-fält) Isocenter sammanfaller.", s5_value, s5_value_detailed, s5_status)); string s6_value = string.Empty; foreach (Beam beam in planSetup.Beams) { if (Operators.LikeString(beam.Id.ToLower(), "Uppl*gg".ToLower(), CompareMethod.Text) == false) { DataTable DRRSetting = AriaInterface.Query("select distinct SliceRT.AcqNote from Radiation, PlanSetup, SliceRT, Slice, Image where PlanSetup.PlanSetupSer =" + planSetupSer.ToString() + " and Radiation.RadiationId ='" + beam.Id + "' and PlanSetup.PlanSetupSer = Radiation.PlanSetupSer and SliceRT.RadiationSer = Radiation.RadiationSer and SliceRT.SliceSer = Slice.SliceSer and SliceRT.AcqNote like 'MultiWindow%'"); if (DRRSetting.Rows.Count == 1 && DRRSetting.Rows[0][0] != DBNull.Value) { // split file path s6_value += (s6_value.Length == 0 ? "" : ", ") + beam.Id + ": " + QueryContents.FindInFiles((string)DRRSetting.Rows[0][0]);//(string)DRRSetting.Rows[0][0]; } } } s6_value = reorderBeamParam(s6_value, ","); checklistItems.Add(new ChecklistItem("S6. Kvalitén på DRR", "Kontrollera att kvalitén på DRR för alla fält som ska ha en DRR är acceptabel.", s6_value, AutoCheckStatus.MANUAL)); string s7_value = string.Empty; AutoCheckStatus s7_status = AutoCheckStatus.MANUAL; List <string> s7_tolerances = new List <string>(); foreach (Beam beam in planSetup.Beams) { string toleranceId = string.Empty; DataTable toleranceIdTable = AriaInterface.Query("select Tolerance.ToleranceId from Radiation,ExternalFieldCommon,Tolerance where ExternalFieldCommon.RadiationSer=Radiation.RadiationSer and ExternalFieldCommon.ToleranceSer=Tolerance.ToleranceSer and Radiation.PlanSetupSer=" + planSetupSer.ToString() + " and Radiation.RadiationId='" + beam.Id + "'"); if (toleranceIdTable.Rows.Count == 1 && toleranceIdTable.Rows[0][0] != DBNull.Value) { s7_tolerances.Add((string)toleranceIdTable.Rows[0][0]); } else { s7_tolerances.Add("Saknas"); } } string[] uniqueTolerances = s7_tolerances.Distinct().ToArray(); if (uniqueTolerances.Length == 1 && String.Equals(s7_tolerances[0], "Saknas", StringComparison.OrdinalIgnoreCase) == false) { s7_value = s7_tolerances[0]; } else if (uniqueTolerances.Length == 1 && String.Equals(s7_tolerances[0], "Saknas", StringComparison.OrdinalIgnoreCase) == false) { s7_value = s7_tolerances[0]; s7_status = AutoCheckStatus.FAIL; } else { foreach (string tolerance in uniqueTolerances) { s7_value += (s7_value.Length == 0 ? string.Empty : ", ") + tolerance; } s7_status = AutoCheckStatus.FAIL; } checklistItems.Add(new ChecklistItem("S7. Toleranstabell har valts korrekt", "Kontrollera att korrekt toleranstabell har använts, baserat på maskintyp (Elekta/Varian), strålkvalité (elektroner/fotoner), nätmask (H&N fix), annan fixation som sitter fast i britsen (fast fix) eller icke fast fixation (utan fix).", s7_value, s7_status)); if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Varian) { string s8_value = string.Empty; string s8_value_detailed = string.Empty; AutoCheckStatus s8_status = AutoCheckStatus.MANUAL; List <string> s8_imageModalityBeam = new List <string>(); List <int> s8_imageModalityBeamNr = new List <int>(); List <string> s8_beamId = new List <string>(); foreach (Beam beam in planSetup.Beams) { if (beam.IsSetupField && !Operators.LikeString(beam.Id.ToLower(), "Uppl*gg".ToLower(), CompareMethod.Text)) { List <string> protocolIds = new List <string>(); s8_value_detailed += (s8_value_detailed.Length == 0 ? string.Empty : "\r\n\r\n") + beam.Id + ": "; s8_value = string.Empty; DataTable dataTableImageSessions = AriaInterface.Query("select Session.SessionNum,SessionProcedure.SessionProcedureTemplateId from SessionProcedurePart,SessionProcedure,Session,Radiation where SessionProcedurePart.RadiationSer=Radiation.RadiationSer and SessionProcedure.SessionProcedureSer=SessionProcedurePart.SessionProcedureSer and Session.SessionSer=SessionProcedure.SessionSer and Radiation.PlanSetupSer=" + planSetupSer.ToString() + " and Radiation.RadiationId='" + beam.Id + "' order by SessionNum"); foreach (DataRow dataRow in dataTableImageSessions.Rows) { int sessionNr = (int)dataRow[0]; string procedureTemplateId = (dataRow[1] == DBNull.Value ? "-" : (string)dataRow[1]); s8_value_detailed += "\r\n Session " + sessionNr.ToString() + ": " + procedureTemplateId; protocolIds.Add(procedureTemplateId); } s8_beamId.Add(beam.Id); string[] uniqueprotocolIds = protocolIds.Distinct().ToArray(); if (uniqueprotocolIds.Length == 0) { s8_imageModalityBeam.Add("Ingen"); s8_status = AutoCheckStatus.FAIL; } else if (uniqueprotocolIds.Length == 1) { s8_imageModalityBeam.Add(uniqueprotocolIds[0]); } else { s8_imageModalityBeam.Add("Flera"); s8_status = AutoCheckStatus.FAIL; } if (fractionation != null && fractionation.NumberOfFractions != protocolIds.Count) { s8_status = AutoCheckStatus.FAIL; } s8_imageModalityBeamNr.Add(protocolIds.Count); } } for (int beamNr = 0; beamNr < s8_imageModalityBeam.Count; beamNr++) { s8_value += (s8_value.Length == 0 ? string.Empty : ", ") + s8_beamId[beamNr] + ": " + s8_imageModalityBeam[beamNr] + " (" + s8_imageModalityBeamNr[beamNr].ToString() + " fr)"; } checklistItems.Add(new ChecklistItem("S8. Bildtagningsmodalitet är korrekt", "Kontrollera att bildtagning är korrekt\r\nVarian:\r\n • Korrekt bildtagningsmodalitet är inlagd, samt att bildtagning är aktiverad för alla sessioner på setup-fälten\r\n • Se bilaga 4 i dokumentet ”Verifikationsbilder”\r\nElekta:\r\n • Inga setup-fält på tangentiella bröstbehandlingar\r\n • Inga setup-fält på L09, L07 och L05 (XVI används i första hand för icke-laterala behandlingar)\r\n • På L03 tas bilder med behandlingsfält om de finns i 0/180° och 90/270°, annars ska det finnas setup-fält", s8_value, s8_value_detailed, s8_status)); } string s9_value = "Saknas"; AutoCheckStatus s9_status = AutoCheckStatus.FAIL; foreach (Beam beam in planSetup.Beams) { if (beam.IsSetupField && Operators.LikeString(beam.Id.ToLower(), "Uppl*gg".ToLower(), CompareMethod.Text)) { s9_status = AutoCheckStatus.PASS; s9_value = "Existerar"; break; } } checklistItems.Add(new ChecklistItem("S9. Uppläggsfält existerar i planen", "Kontrollera att det finns ett fält med Id Upplägg i behandlingsplanen", s9_value, s9_status)); checklistItems.Add(new ChecklistItem("S10. Uppläggningen är genomförbar", "Kontrollera att uppläggningen är genomförbar för den givna geometrin \r\n • Exempelvis att behandlingar av extremt kaudala target inte är orienterade 'Head First'", "", AutoCheckStatus.MANUAL)); }
public ChecklistItem(string shortInfo, string detailedInfo, string shortResult, AutoCheckStatus autoCheckStatus) : this(shortInfo, detailedInfo, shortResult, null, autoCheckStatus) { }
public void S() { checklistItems.Add(new ChecklistItem("S. Setup")); if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Varian) { string s1_value = string.Empty; AutoCheckStatus s1_status = AutoCheckStatus.FAIL; int s1_numberOfPass = 0; int s1_numberOfWarnings = 0; foreach (Beam beam in planSetup.Beams) { double iduPosVrt = double.NaN; DataTable dataTableIDUPosVrt = AriaInterface.Query("select IDUPosVrt from Radiation,ExternalFieldCommon where ExternalFieldCommon.RadiationSer=Radiation.RadiationSer and Radiation.PlanSetupSer=" + planSetupSer.ToString() + " and Radiation.RadiationId='" + beam.Id + "'"); if (dataTableIDUPosVrt.Rows.Count == 1 && dataTableIDUPosVrt.Rows[0][0] != DBNull.Value && !Operators.LikeString(beam.Id.ToLower(), "Uppl*gg".ToLower(), CompareMethod.Text)) { iduPosVrt = (double)dataTableIDUPosVrt.Rows[0][0]; if (iduPosVrt == -50) { s1_numberOfPass++; } else { s1_numberOfWarnings++; } } else if (Operators.LikeString(beam.Id.ToLower(), "Uppl*gg".ToLower(), CompareMethod.Text)) // Field with Id Upplägg may have any ImageVrt Position, even undefined. { s1_numberOfPass++; } s1_value += (s1_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": " + (double.IsNaN(iduPosVrt) ? "-" : ((double)iduPosVrt).ToString("0.0") + " cm"); } if (s1_numberOfPass == numberOfBeams) { s1_status = AutoCheckStatus.PASS; } else if (s1_numberOfPass + s1_numberOfWarnings == numberOfBeams) { s1_status = AutoCheckStatus.WARNING; } s1_value = reorderBeamParam(s1_value, ","); checklistItems.Add(new ChecklistItem("S1. Bildplattans vertikala position är -50 cm om inte särskilda skäl föreligger", "Kontrollera att bildplattans vertikala position är -50 cm om inte särskilda skäl föreligger", s1_value, s1_status)); } if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Varian) { string s2_value = string.Empty; AutoCheckStatus s2_status = AutoCheckStatus.FAIL; int s2_numberOfPass = 0; List <double> s2_gantryAngles = new List <double>(); List <bool> s2_gantryAnglesExtended = new List <bool>(); foreach (Beam beam in planSetup.Beams) { if (!beam.IsSetupField) { DataTable dataTableExtended = AriaInterface.Query("select GantryRtnExt from ExternalField,Radiation where GantryRtnExt='EN' and ExternalField.RadiationSer=Radiation.RadiationSer and Radiation.PlanSetupSer=" + planSetupSer.ToString() + " and Radiation.RadiationId='" + beam.Id + "'"); bool extended = (dataTableExtended.Rows.Count == 1); s2_gantryAnglesExtended.Add(extended); s2_gantryAngles.Add(beam.ControlPoints[0].GantryAngle); s2_value += (s2_value.Length == 0 ? "Sida: " + treatmentSide.ToString() + ", " : ", ") + beam.Id + ": " + beam.ControlPoints[0].GantryAngle.ToString("0.0") + (extended ? "E" : string.Empty); } } if (checklistType == ChecklistType.EclipseVMAT) { foreach (bool extended in s2_gantryAnglesExtended) { if (!extended) { s2_numberOfPass++; } } } else if (treatmentSide == TreatmentSide.PlusX) { bool fieldsOnOppositeSide = false; foreach (double angle in s2_gantryAngles) { if (angle > 185 && angle <= 290) { fieldsOnOppositeSide = true; } } for (int beamNr = 0; beamNr < s2_gantryAngles.Count; beamNr++) { if (fieldsOnOppositeSide) { if (s2_gantryAnglesExtended[beamNr] == false) { s2_numberOfPass++; } } else { if (s2_gantryAngles[beamNr] > 180 && s2_gantryAngles[beamNr] <= 185) { if (s2_gantryAnglesExtended[beamNr] == true) { s2_numberOfPass++; } } else { if (s2_gantryAnglesExtended[beamNr] == false) { s2_numberOfPass++; } } } } } else { bool fieldsOnOppositeSide = false; foreach (double angle in s2_gantryAngles) { if (angle >= 70 && angle < 175) { fieldsOnOppositeSide = true; } } for (int beamNr = 0; beamNr < s2_gantryAngles.Count; beamNr++) { if (fieldsOnOppositeSide) { if (s2_gantryAnglesExtended[beamNr] == false) { s2_numberOfPass++; } } else { if (s2_gantryAngles[beamNr] >= 175 && s2_gantryAngles[beamNr] <= 180) { if (s2_gantryAnglesExtended[beamNr] == true) { s2_numberOfPass++; } } else { if (s2_gantryAnglesExtended[beamNr] == false) { s2_numberOfPass++; } } } } } if (s2_numberOfPass == numberOfTreatmentBeams) { s2_status = AutoCheckStatus.PASS; } s2_value = reorderBeamParam(s2_value, ","); checklistItems.Add(new ChecklistItem("S2. Extended har valts korrekt på behandlingsfält", "Kontrollera att Extended har valts korrekt på behandlingsfält. Om det finns fält som går över långt på motstående sida (70° resp. 290°) ska inga fält ha Extended. I annat fall gäller:\r\n • Vänstersidiga behandlingar: Fält med 180<gantryvinkel<=185 ska ha Extended\r\n • Högersidiga behandlingar: Fält med 175<=gantryvinkel<=180 ska ha Extended\r\n • Övriga fält ska ej ha Extended", s2_value, s2_status)); } string s3_value = string.Empty; AutoCheckStatus s3_status = AutoCheckStatus.UNKNOWN; List <double> s3_setupFieldAngles = new List <double>(); List <string> s3_beamIds = new List <string>(); foreach (Beam beam in planSetup.Beams) { if (beam.IsSetupField && !Operators.LikeString(beam.Id.ToLower(), "Uppl*gg".ToLower(), CompareMethod.Text)) { s3_setupFieldAngles.Add(beam.ControlPoints[0].GantryAngle); s3_beamIds.Add(beam.Id.ToLower()); s3_value += (s3_value.Length == 0 ? "Sida: " + treatmentSide.ToString() + ", " : ", ") + beam.Id + ": " + beam.ControlPoints[0].GantryAngle.ToString("0.0"); } } int s3_cbctIndex = s3_beamIds.IndexOf("cbct"); if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Varian) { if (s3_setupFieldAngles.Count == 0) { s3_value = "Setupfält saknas i planen"; } else if (s3_setupFieldAngles.Count == 1 && s3_cbctIndex == 0) { s3_status = AutoCheckStatus.PASS; } else if (s3_setupFieldAngles.Count == 3) { // Ta bort CBCT från fortsatta kontrollen if (s3_cbctIndex != -1) { s3_beamIds.RemoveAt(s3_cbctIndex); s3_setupFieldAngles.RemoveAt(s3_cbctIndex); } } if (s3_setupFieldAngles.Count == 2) { if (treatmentSide == TreatmentSide.PlusX) // Vänstersidig { if (s3_setupFieldAngles.IndexOf(0) != -1 && s3_setupFieldAngles.IndexOf(270) != -1) { s3_status = AutoCheckStatus.PASS; } else { s3_status = AutoCheckStatus.FAIL; } } else // Högersidig { if (s3_setupFieldAngles.IndexOf(180) != -1 && s3_setupFieldAngles.IndexOf(270) != -1) { s3_status = AutoCheckStatus.PASS; } else { s3_status = AutoCheckStatus.FAIL; } } } } else { s3_status = AutoCheckStatus.MANUAL; // If not Varian, then do manual check of setup angles. } s3_value = reorderBeamParam(s3_value, ","); checklistItems.Add(new ChecklistItem("S3. Gantryvinklar för setupfälten är korrekta", "Kontrollera att setupfältens gantryvinklar är korrekta (patientgeometrin avgör vinklar)\r\n • Standard: 270° respektive 0°\r\n• • Högersidiga behandlingar: 180° respektive 270°", s3_value, s3_status)); string s4_value; AutoCheckStatus s4_status = AutoCheckStatus.FAIL; int s4_numberOfPass = 0; bool s4_isocenterCouldNotBeDetermined = false; VVector s4_isocenterPosition = new VVector(double.NaN, double.NaN, double.NaN); foreach (Beam beam in planSetup.Beams) { if (double.IsNaN(s4_isocenterPosition.x) && double.IsNaN(s4_isocenterPosition.y) && double.IsNaN(s4_isocenterPosition.z)) { s4_isocenterPosition = beam.IsocenterPosition; if (double.IsNaN(beam.IsocenterPosition.x) == false && double.IsNaN(beam.IsocenterPosition.y) == false && double.IsNaN(beam.IsocenterPosition.z) == false) { s4_numberOfPass++; } else { s4_isocenterCouldNotBeDetermined = true; } } else if (Math.Round(s4_isocenterPosition.x, 1) == Math.Round(beam.IsocenterPosition.x, 1) && Math.Round(s4_isocenterPosition.y, 1) == Math.Round(beam.IsocenterPosition.y, 1) && Math.Round(s4_isocenterPosition.z, 1) == Math.Round(beam.IsocenterPosition.z, 1)) { s4_numberOfPass++; } } if (s4_numberOfPass == numberOfBeams)//numberOfTreatmentBeams) { s4_value = "Samma isocenter"; s4_status = AutoCheckStatus.PASS; } else if (s4_isocenterCouldNotBeDetermined) { s4_value = "Isocenterposition kunde ej bestämmas"; s4_status = AutoCheckStatus.WARNING; } else { s4_value = "Olika isocenter mellan fälten"; s4_status = AutoCheckStatus.WARNING; } checklistItems.Add(new ChecklistItem("S4. Alla fält har samma isocenter vid isocentrisk teknik", "Kontrollera att samtliga fälts (inklusive setup-fält) Isocenter sammanfaller.", s4_value, s4_status)); string s5_value = string.Empty; foreach (Beam beam in planSetup.Beams) { if (Operators.LikeString(beam.Id.ToLower(), "Uppl*gg".ToLower(), CompareMethod.Text) == false) { DataTable DRRSetting = AriaInterface.Query("select distinct SliceRT.AcqNote from Radiation, PlanSetup, SliceRT, Slice, Image where PlanSetup.PlanSetupSer =" + planSetupSer.ToString() + " and Radiation.RadiationId ='" + beam.Id + "' and PlanSetup.PlanSetupSer = Radiation.PlanSetupSer and SliceRT.RadiationSer = Radiation.RadiationSer and SliceRT.SliceSer = Slice.SliceSer and SliceRT.AcqNote like 'MultiWindow%'"); if (DRRSetting.Rows.Count == 1 && DRRSetting.Rows[0][0] != DBNull.Value) { // split file path s5_value += (s5_value.Length == 0 ? "" : ", ") + beam.Id + ": " + QueryContents.FindInFiles((string)DRRSetting.Rows[0][0]);//(string)DRRSetting.Rows[0][0]; } } } s5_value = reorderBeamParam(s5_value, ","); checklistItems.Add(new ChecklistItem("S5. Kvalitén på DRR", "Kontrollera att kvalitén på DRR för alla fält som ska ha en DRR är acceptabel.", s5_value, AutoCheckStatus.MANUAL)); string s6_value = string.Empty; AutoCheckStatus s6_status = AutoCheckStatus.MANUAL; List <string> s6_tolerances = new List <string>(); foreach (Beam beam in planSetup.Beams) { string toleranceId = string.Empty; DataTable toleranceIdTable = AriaInterface.Query("select Tolerance.ToleranceId from Radiation,ExternalFieldCommon,Tolerance where ExternalFieldCommon.RadiationSer=Radiation.RadiationSer and ExternalFieldCommon.ToleranceSer=Tolerance.ToleranceSer and Radiation.PlanSetupSer=" + planSetupSer.ToString() + " and Radiation.RadiationId='" + beam.Id + "'"); if (toleranceIdTable.Rows.Count == 1 && toleranceIdTable.Rows[0][0] != DBNull.Value) { s6_tolerances.Add((string)toleranceIdTable.Rows[0][0]); } else { s6_tolerances.Add("Saknas"); } } string[] uniqueTolerances = s6_tolerances.Distinct().ToArray(); if (uniqueTolerances.Length == 1 && String.Equals(s6_tolerances[0], "Saknas", StringComparison.OrdinalIgnoreCase) == false) { s6_value = s6_tolerances[0]; } else if (uniqueTolerances.Length == 1 && String.Equals(s6_tolerances[0], "Saknas", StringComparison.OrdinalIgnoreCase) == false) { s6_value = s6_tolerances[0]; s6_status = AutoCheckStatus.FAIL; } else { foreach (string tolerance in uniqueTolerances) { s6_value += (s6_value.Length == 0 ? string.Empty : ", ") + tolerance; } s6_status = AutoCheckStatus.FAIL; } checklistItems.Add(new ChecklistItem("S6. Toleranstabell har valts korrekt", "Kontrollera att korrekt toleranstabell har använts, baserat på maskintyp (Elekta/Varian), strålkvalité (elektroner/fotoner), nätmask (H&N fix), annan fixation som sitter fast i britsen (fast fix) eller icke fast fixation (utan fix).", s6_value, s6_status)); if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Varian) { string s7_value = string.Empty; string s7_value_detailed = string.Empty; AutoCheckStatus s7_status = AutoCheckStatus.MANUAL; List <string> s7_imageModalityBeam = new List <string>(); List <int> s7_imageModalityBeamNr = new List <int>(); List <string> s7_beamId = new List <string>(); foreach (Beam beam in planSetup.Beams) { if (beam.IsSetupField && !Operators.LikeString(beam.Id.ToLower(), "Uppl*gg".ToLower(), CompareMethod.Text)) { List <string> protocolIds = new List <string>(); s7_value_detailed += (s7_value_detailed.Length == 0 ? string.Empty : "\r\n\r\n") + beam.Id + ": "; s7_value = string.Empty; DataTable dataTableImageSessions = AriaInterface.Query("select Session.SessionNum,SessionProcedure.SessionProcedureTemplateId from SessionProcedurePart,SessionProcedure,Session,Radiation where SessionProcedurePart.RadiationSer=Radiation.RadiationSer and SessionProcedure.SessionProcedureSer=SessionProcedurePart.SessionProcedureSer and Session.SessionSer=SessionProcedure.SessionSer and Radiation.PlanSetupSer=" + planSetupSer.ToString() + " and Radiation.RadiationId='" + beam.Id + "' order by SessionNum"); foreach (DataRow dataRow in dataTableImageSessions.Rows) { int sessionNr = (int)dataRow[0]; string procedureTemplateId = (dataRow[1] == DBNull.Value ? "-" : (string)dataRow[1]); s7_value_detailed += "\r\n Session " + sessionNr.ToString() + ": " + procedureTemplateId; protocolIds.Add(procedureTemplateId); } s7_beamId.Add(beam.Id); string[] uniqueprotocolIds = protocolIds.Distinct().ToArray(); if (uniqueprotocolIds.Length == 0) { s7_imageModalityBeam.Add("Ingen"); s7_status = AutoCheckStatus.FAIL; } else if (uniqueprotocolIds.Length == 1) { s7_imageModalityBeam.Add(uniqueprotocolIds[0]); } else { s7_imageModalityBeam.Add("Flera"); s7_status = AutoCheckStatus.FAIL; } if (fractionation != null && fractionation.NumberOfFractions != protocolIds.Count) { s7_status = AutoCheckStatus.FAIL; } s7_imageModalityBeamNr.Add(protocolIds.Count); } } for (int beamNr = 0; beamNr < s7_imageModalityBeam.Count; beamNr++) { s7_value += (s7_value.Length == 0 ? string.Empty : ", ") + s7_beamId[beamNr] + ": " + s7_imageModalityBeam[beamNr] + " (" + s7_imageModalityBeamNr[beamNr].ToString() + " fr)"; } checklistItems.Add(new ChecklistItem("S7. Bildtagningsmodalitet är korrekt", "Kontrollera att bildtagning är korrekt\r\nVarian:\r\n • Korrekt bildtagningsmodalitet är inlagd, samt att bildtagning är aktiverad för alla sessioner på setup-fälten\r\n • Se bilaga 4 i dokumentet ”Verifikationsbilder”\r\nElekta:\r\n • Inga setup-fält på tangentiella bröstbehandlingar\r\n • Inga setup-fält på L09, L07 och L05 (XVI används i första hand för icke-laterala behandlingar)\r\n • På L03 tas bilder med behandlingsfält om de finns i 0/180° och 90/270°, annars ska det finnas setup-fält", s7_value, s7_value_detailed, s7_status)); } string s8_value = "Saknas"; AutoCheckStatus s8_status = AutoCheckStatus.FAIL; foreach (Beam beam in planSetup.Beams) { if (beam.IsSetupField && Operators.LikeString(beam.Id.ToLower(), "Uppl*gg".ToLower(), CompareMethod.Text)) { s8_status = AutoCheckStatus.PASS; s8_value = "Existerar"; break; } } checklistItems.Add(new ChecklistItem("S8. Uppläggsfält existerar i planen", "Kontrollera att det finns ett fält med Id Upplägg i behandlingsplanen", s8_value, s8_status)); checklistItems.Add(new ChecklistItem("S9. Uppläggningen är genomförbar", "Kontrollera att upplägget är genomförbart för den givna geometrin \r\n • Exempelvis att behandlingar av extremt kaudala target inte är orienterade 'Head First'", "", AutoCheckStatus.MANUAL)); }
public void C() //Conformal Arc { if (checklistType == ChecklistType.EclipseConformal) { checklistItems.Add(new ChecklistItem("C. Conformal Arc")); string c1_value = string.Empty; AutoCheckStatus c1_status = AutoCheckStatus.FAIL; int c1_numberOfWarnings = 0; int c1_numberOfPass = 0; List <double> collAngles = new List <double>(); foreach (Beam beam in planSetup.Beams) { if (!beam.IsSetupField) { double collimatorAngle = beam.ControlPoints[0].CollimatorAngle; collAngles.Add(collimatorAngle); if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Varian) { if (collimatorAngle >= 0 && collimatorAngle <= 5) { c1_numberOfPass++; } else if (collimatorAngle > 5 && collimatorAngle < 355) { c1_numberOfWarnings++; } } c1_value += (c1_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": " + collimatorAngle.ToString("0.0") + "°"; } } if (c1_numberOfPass == numberOfTreatmentBeams) { c1_status = AutoCheckStatus.PASS; } else if (c1_numberOfPass + c1_numberOfWarnings == numberOfTreatmentBeams) { c1_status = AutoCheckStatus.WARNING; } if (collAngles.Count > 1 && collAngles.Distinct().ToList().Count < 2) { c1_status = AutoCheckStatus.FAIL; } checklistItems.Add(new ChecklistItem("C1. Kollimatorvinkeln är lämplig", "Kontrollera att kollimatorvinkeln är lämplig\r\n • Varian Conformal Arc: vanligtvis 0° till 5°, men passar detta ej PTV är andra vinklar ok", c1_value, c1_status)); string c2_value = string.Empty; AutoCheckStatus c2_status = AutoCheckStatus.WARNING; int c2_numberOfPass = 0; foreach (Beam beam in planSetup.Beams) { if (!beam.IsSetupField) { double jawLim = 2.5; double X1 = 0.1 * beam.ControlPoints[0].JawPositions.X1; double X2 = 0.1 * beam.ControlPoints[0].JawPositions.X2; double Y1 = 0.1 * beam.ControlPoints[0].JawPositions.Y1; double Y2 = 0.1 * beam.ControlPoints[0].JawPositions.Y2; if ((Math.Abs(X1) <= jawLim && Math.Abs(X2) <= jawLim && Math.Abs(Y1) <= jawLim && Math.Abs(Y2) <= jawLim) && (beam.EnergyModeDisplayName == "6X-FFF" || beam.EnergyModeDisplayName == "10X-FFF")) { c2_numberOfPass++; } else if ((Math.Abs(X1) > jawLim || Math.Abs(X2) > jawLim || Math.Abs(Y1) > jawLim || Math.Abs(Y2) > jawLim) && (beam.EnergyModeDisplayName == "6X" || beam.EnergyModeDisplayName == "10X")) { c2_numberOfPass++; } c2_value += (c2_value.Length == 0 ? string.Empty : ", ") + beam.Id + " " + beam.EnergyModeDisplayName + ": X1: " + X1.ToString("0.0") + " cm, X2: " + X2.ToString("0.0") + " cm, Y1: " + Y1.ToString("0.0") + " cm, Y2: " + Y2.ToString("0.0") + " cm"; } } if (c2_numberOfPass == numberOfTreatmentBeams) { c2_status = AutoCheckStatus.PASS; } checklistItems.Add(new ChecklistItem("C2. Fältbredden är rimlig ", "Kontrollera att fältet har en rimlig fältbredd och har korrekt behandlingsteknik FF eller FFF (riktvärde <= 5 cm, vid större fältbredd ska FF användas).", c2_value, c2_status)); string c3_value = string.Empty; AutoCheckStatus c3_status = AutoCheckStatus.WARNING; string c4_value = string.Empty; AutoCheckStatus c4_status = AutoCheckStatus.MANUAL; //int c3_numberOfPass = 0; double totArcLength = 0; foreach (Beam beam in planSetup.Beams) { if (!beam.IsSetupField) { //double arclength = beam.ControlPoints[0].GantryAngle - beam.ControlPoints[beam.ControlPoints.Count()].GantryAngle; // Kombinerad summering av arcLength samt kontroll av korrekt MLC-inställning samt behandlingsteknik. totArcLength += beam.ArcLength; c3_value += (c3_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": " + beam.ArcLength.ToString("0.0") + "°"; if (beam.MLCPlanType.ToString().IndexOf("ArcDynamic") == -1 && beam.Technique.ToString().IndexOf("SRS ARC") == -1) { c4_value += "Behandlingsteknik är " + beam.Technique.ToString() + " och MLC-inställningar är " + beam.MLCPlanType.ToString() + ", korrigera till SRS ARC respektive Arc Dynamic. Tag bort och lägg till MLC igen och gör om Fit To Structure."; c4_status = AutoCheckStatus.FAIL; } else if (beam.MLCPlanType.ToString().IndexOf("ArcDynamic") == -1 && beam.Technique.ToString().IndexOf("SRS ARC") > -1) { c4_value += "MLC-inställningar är " + beam.MLCPlanType.ToString() + ", korrigera till Arc Dynamic. Tag bort och lägg till MLC igen och gör om Fit To Structure."; c4_status = AutoCheckStatus.FAIL; } else if (beam.Technique.ToString().IndexOf("SRS ARC") == -1 && beam.MLCPlanType.ToString().IndexOf("ArcDynamic") > -1) { c4_value += "Behandlingsteknik är " + beam.Technique.ToString() + ", korrigera till SRS ARC."; c4_status = AutoCheckStatus.FAIL; } } } c3_value += ". Total längd: " + totArcLength.ToString("0.0"); if (totArcLength > 180) { c3_status = AutoCheckStatus.PASS; } checklistItems.Add(new ChecklistItem("C3. Arclängd är rimlig", "Kontrollera att totala längden på arcs är mer än 180°)", c3_value, c3_status)); // No Jawtracking for Conformal arcs //var v5_values = CheckJawTracking(planSetup); // Values here is set in the loop for c3. checklistItems.Add(new ChecklistItem("C4. Leveransmönstret är rimligt", "Kontrollera visuellt att leveransmönstret är rimligt (att MLCn går längs Z_PTV-konturen)", c4_value, c4_status)); } }
public void P() { checklistItems.Add(new ChecklistItem("P. Dosplan")); string p1_value = string.Empty; string p1_value_detail = string.Empty; bool reviewed = false; AutoCheckStatus p1_status = AutoCheckStatus.MANUAL; //CheckResult(planSetup.ApprovalStatus == PlanSetupApprovalStatus.PlanningApproved); //DataTable history = AriaInterface.Query("SELECT DISTINCT HistoricalStatus=Approval.Status, HistoricalStatusDate=Approval.StatusDate, HistoricalStatusUserId=Approval.StatusUserName, HistoricalStatusUserName=CONCAT((SELECT DISTINCT Staff.AliasName FROM Staff WHERE Staff.StaffId=Approval.StatusUserName), (SELECT DISTINCT Doctor.AliasName FROM Doctor WHERE Doctor.DoctorId=Approval.StatusUserName)) FROM Approval, PlanSetup, Staff WHERE PlanSetup.PlanSetupSer=Approval.TypeSer AND Approval.ApprovalType='PlanSetup' and PlanSetup.PlanSetupSer = '" + planSetupSer.ToString() + "' ORDER BY HistoricalStatusDate"); DataTable history = AriaInterface.Query("SELECT DISTINCT HistoricalStatus=Approval.Status, HistoricalStatusDate=Approval.StatusDate, HistoricalStatusUserId=Approval.StatusUserName, HistoricalStatusUserName=CONCAT((SELECT DISTINCT Staff.AliasName FROM Staff WHERE Staff.StaffId=Approval.StatusUserName), (SELECT DISTINCT Doctor.AliasName FROM Doctor WHERE Doctor.DoctorId=Approval.StatusUserName)) FROM Approval, PlanSetup, Staff WHERE PlanSetup.PlanSetupSer=Approval.TypeSer AND Approval.ApprovalType='PlanSetup' and PlanSetup.PlanSetupSer = '" + planSetupSer.ToString() + "' UNION SELECT DISTINCT HistoricalStatus=PlanSetup.Status, HistoricalStatusDate=PlanSetup.StatusDate, HistoricalStatusUserId=PlanSetup.StatusUserName, HistoricalStatusUserName=CONCAT((SELECT DISTINCT Staff.AliasName FROM Staff WHERE Staff.StaffId=PlanSetup.StatusUserName), (SELECT DISTINCT Doctor.AliasName FROM Doctor WHERE Doctor.DoctorId=PlanSetup.StatusUserName)) FROM PlanSetup WHERE PlanSetup.PlanSetupSer= '" + planSetupSer.ToString() + "' ORDER BY HistoricalStatusDate"); if (history.Rows.Count > 0) { p1_value_detail += "Historisk Status\tTid\t\t\tUserId\tUserName\r\n"; } DataRow lastRow = history.Rows[history.Rows.Count - 1]; // except status from last row, since it will be added explicitly foreach (DataRow row in history.Rows) { if (String.Equals((string)row["HistoricalStatus"], "Reviewed")) { reviewed = true; } if (row != lastRow) { p1_value += (string)row["HistoricalStatus"] + ", "; } p1_value_detail += (string)row["HistoricalStatus"] + "\t" + row["HistoricalStatusDate"].ToString() + "\t" + (string)row["HistoricalStatusUserId"]; if (row["HistoricalStatusUserName"] == DBNull.Value) { p1_value_detail += "\r\n"; } else { p1_value_detail += "\t" + (string)row["HistoricalStatusUserName"] + "\r\n"; } } p1_value += planSetup.ApprovalStatus.ToString(); if (String.Equals(planSetup.ApprovalStatus.ToString(), "PlanningApproved") == false) { p1_status = AutoCheckStatus.FAIL; p1_value += ", planen har fel status"; } else if (reviewed == false) { p1_status = AutoCheckStatus.FAIL; p1_value += ", planen är inte Reviewed"; } else { if (history.Rows.Count == 3 && String.Equals((string)history.Rows[1]["HistoricalStatus"], "Reviewed")) { p1_status = AutoCheckStatus.PASS; } } if (String.IsNullOrEmpty(p1_value_detail) == true) { checklistItems.Add(new ChecklistItem("P1. Planens status är korrekt", "Kontrollera att planen är Planning Approved i Aria samt att den tidigare haft status Reviewed", p1_value, p1_status)); } else { checklistItems.Add(new ChecklistItem("P1. Planens status är korrekt", "Kontrollera att planen är Planning Approved i Aria samt att den tidigare haft status Reviewed", p1_value, p1_value_detail, p1_status)); } string p2_value = string.Empty; AutoCheckStatus p2_status = AutoCheckStatus.FAIL; if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Multiple) { p2_status = AutoCheckStatus.WARNING; } int p2_numberOfPass = 0; foreach (Beam beam in planSetup.Beams) { bool fff = (beam.EnergyModeDisplayName.IndexOf("FFF") != -1); if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Varian) { if (fff == false && beam.DoseRate == 600) { p2_numberOfPass++; } else if (fff == true && beam.EnergyModeDisplayName.IndexOf("6") == 0 && beam.DoseRate == 1400) { p2_numberOfPass++; } else if (fff == true && beam.EnergyModeDisplayName.IndexOf("10") == 0 && beam.DoseRate == 2400) { p2_numberOfPass++; } } else if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Elekta) { if (beam.EnergyModeDisplayName.IndexOf("4") == 0 && beam.DoseRate == 250) { p2_numberOfPass++; } else if (beam.EnergyModeDisplayName.IndexOf("6") == 0 && beam.DoseRate == 600) { p2_numberOfPass++; } else if (beam.EnergyModeDisplayName.IndexOf("10") == 0 && beam.DoseRate == 500) { p2_numberOfPass++; } /*{ * if (beam.TreatmentUnit.Id.IndexOf("L05") == -1 && beam.DoseRate == 400) * p2_numberOfPass++; * else if (beam.TreatmentUnit.Id.IndexOf("L05") != -1 && beam.DoseRate == 500) * p2_numberOfPass++; * }*/ } p2_value += (p2_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": " + beam.DoseRate.ToString(); } if (p2_numberOfPass == numberOfBeams) { p2_status = AutoCheckStatus.PASS; } p2_value = reorderBeamParam(p2_value, ","); checklistItems.Add(new ChecklistItem("P2. Dosraten är korrekt", "Kontrollera att dosraten (MU/min) är korrekt:\r\n • Varian: 600 (ej FFF), 1400 (6 MV FFF), 2400 (10 MV FFF)\r\n • Elekta: 250 (4 MV), 600 (6 MV), 500 (10 MV)", p2_value, p2_status)); if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Varian) { string p3_value = string.Empty; AutoCheckStatus p3_status = AutoCheckStatus.FAIL; int p3_numberOfPass = 0; foreach (Beam beam in planSetup.Beams) { if (!beam.IsSetupField) { double treatmentTime = double.NaN; DataTable treatmentTimeTable = AriaInterface.Query("select TreatmentTime from Radiation,ExternalFieldCommon where ExternalFieldCommon.RadiationSer=Radiation.RadiationSer and Radiation.PlanSetupSer=" + planSetupSer.ToString() + " and Radiation.RadiationId='" + beam.Id + "'"); if (treatmentTimeTable.Rows.Count == 1 && treatmentTimeTable.Rows[0][0] != DBNull.Value) { treatmentTime = (double)treatmentTimeTable.Rows[0][0]; double openMU; double wedgedMU; GetMU(beam, out openMU, out wedgedMU); if (beam.EnergyModeDisplayName.IndexOf("FFF") != -1 && !(checklistType == ChecklistType.EclipseConformal) && !(checklistType == ChecklistType.EclipseVMAT)) { if (openMU < 600 && treatmentTime == 0.5) { p3_numberOfPass++; } } else if ((checklistType == ChecklistType.EclipseVMAT || checklistType == ChecklistType.EclipseConformal) && beam.EnergyModeDisplayName.IndexOf("FFF") == -1) { // For VMAT use treatmentTime=2 if MU<=400, otherwise treatmentTime = min(5.0, max(3.0, ceil(MU/(DoseRate/N)))), where N=2 if (openMU <= 400 && treatmentTime == 2) { p3_numberOfPass++; } if (openMU > 400 && openMU <= 900 && treatmentTime == 3) { p3_numberOfPass++; } if (openMU > 900 && openMU <= 1200 && treatmentTime == 4) { p3_numberOfPass++; } if (openMU > 1200 && openMU <= 2000 && treatmentTime == 5) { p3_numberOfPass++; } if (openMU > 2000 && treatmentTime == 7) { p3_numberOfPass++; } /* * New Old 2018-07-02 commented. * int doseRateFactor = 2; * if (openMU > 400 && treatmentTime == Math.Min(5.0, Math.Max(3.0, Math.Ceiling(openMU / ((double)beam.DoseRate / doseRateFactor))))) * p3_numberOfPass++; * * Old * if (openMU <= 400 && treatmentTime == 2) * p3_numberOfPass++; * else if (openMU > 400 && treatmentTime == 3) * p3_numberOfPass++; */ } else if (checklistType == ChecklistType.EclipseVMAT && beam.EnergyModeDisplayName.IndexOf("FFF") != -1) // Specific Check for Hypo and SRS-brain. { int doseRateFactor = 2; double estTime = openMU / ((double)beam.DoseRate / doseRateFactor); if (planSetup.UniqueFractionation.NumberOfFractions == 7 && planSetup.UniqueFractionation.PrescribedDosePerFraction.Dose > 6) { if (planSetup.Beams.Where(v => v.IsSetupField == false).Count() == 1 && treatmentTime == 3) { p3_numberOfPass++; } else if (planSetup.Beams.Where(v => v.IsSetupField == false).Count() == 2 && treatmentTime == 1.5) { p3_numberOfPass++; } } if (planSetup.UniqueFractionation.NumberOfFractions == 3 && planSetup.UniqueFractionation.PrescribedDosePerFraction.Dose > 9) { if (planSetup.Beams.Where(v => v.IsSetupField == false).Count() == 1 && treatmentTime == 3) { p3_numberOfPass++; } else if (planSetup.Beams.Where(v => v.IsSetupField == false).Count() == 2 && treatmentTime == 1.5) { p3_numberOfPass++; } } } else if (checklistType == ChecklistType.EclipseGating || (checklistType == ChecklistType.EclipseConformal && beam.EnergyModeDisplayName.IndexOf("FFF") != -1)) { if (treatmentTime == 5) { p3_numberOfPass++; } } else { if (openMU <= 500 && wedgedMU == 0 && treatmentTime == 1 || wedgedMU > 0 && wedgedMU <= 300 && treatmentTime == 1) { p3_numberOfPass++; } else if (openMU > 500 && wedgedMU == 0 && treatmentTime == 2 || wedgedMU > 300 && treatmentTime == 2) { p3_numberOfPass++; } } } p3_value += (p3_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": " + (double.IsNaN(treatmentTime) ? "-" : treatmentTime.ToString() + " min"); } } if (p3_numberOfPass == numberOfTreatmentBeams) { p3_status = AutoCheckStatus.PASS; } p3_value = reorderBeamParam(p3_value, ","); checklistItems.Add(new ChecklistItem("P3. Beam on-tiderna är korrekta", "Kontrollera att fälten är tilldelade korrekta beam on-tider:\r\n • 0.5 min för FFF fält med <600 MU\r\n • 1 min för öppna fält med <=500 MU och kilfält med <=300 MU\r\n • 2 min för öppna fält med >500 MU och kilfält med >300 MU \r\n • 5 min för gating\r\n • 5 min för cArc FFF\r\n • För RA/cArc gäller* \r\n\t MU \t\tBeam on (min)\r\n\t <= 400\t\t2\r\n\t 400<x<=900\t3\r\n\t 900<x<=1200\t4\r\n\t 1200<x<=2000\t5\r\n\t >2000\t\t7\r\n*Om dosraten är 600 MU/min. \r\n • För Hypo/hjärna FFF gäller: \r\n\t Antal arcs \tBeam on (min)\r\n\t 1\t\t3\r\n\t 2\t\t1.5\r\n\t", p3_value, p3_status)); } // Will now use information from Prescription rather than verifying against ChecklistType string p4_value = string.Empty; AutoCheckStatus p4_status = AutoCheckStatus.FAIL; string prescribedGating = string.Empty; DataTable dataTableUseGated = AriaInterface.Query("select distinct ExternalFieldCommon.MotionCompTechnique from Radiation,ExternalFieldCommon where Radiation.RadiationSer=ExternalFieldCommon.RadiationSer and Radiation.PlanSetupSer=" + planSetupSer.ToString()); bool gatingChecked; if (dataTableUseGated.Rows.Count == 1 && dataTableUseGated.Rows[0][0] != DBNull.Value && string.Compare((string)dataTableUseGated.Rows[0][0], "GATING") == 0) { gatingChecked = true; p4_value = "Ikryssad"; } else { gatingChecked = false; p4_value = "Ej ikryssad"; } if (prescSer > 0) { DataTable prescription = AriaInterface.Query("select Gating from Prescription, PlanSetup where PlanSetup.PrescriptionSer = Prescription.PrescriptionSer and PlanSetup.PlanSetupSer = '" + planSetupSer.ToString() + "'"); if (prescription.Rows[0]["Gating"] != DBNull.Value) { prescribedGating = (string)prescription.Rows[0]["Gating"]; } p4_status = CheckResult(String.IsNullOrEmpty(prescribedGating) != gatingChecked); } else { p4_status = AutoCheckStatus.WARNING; p4_value = "Ordination saknas - kontroll kan ej genomföras; " + p4_value; } /* * { * if (String.IsNullOrEmpty(prescribedGating) == false) * p4_status = AutoCheckStatus.PASS; * else * p4_status = AutoCheckStatus.FAIL; * p4_value = "Ikryssad"; * } * else * { * if (String.IsNullOrEmpty(prescribedGating) == false) * p4_status = AutoCheckStatus.FAIL; * else * p4_status = AutoCheckStatus.PASS; * p4_value = "Ej ikryssad"; * } */ /* * if (dataTableUseGated.Rows.Count == 1 && dataTableUseGated.Rows[0][0] != DBNull.Value && string.Compare((string)dataTableUseGated.Rows[0][0], "GATING") == 0) * { * if (checklistType == ChecklistType.EclipseGating) * p4_status = AutoCheckStatus.PASS; * else * p4_status = AutoCheckStatus.FAIL; * p4_value = "Ikryssad"; * } * else * { * if (checklistType == ChecklistType.EclipseGating) * p4_status = AutoCheckStatus.FAIL; * else * p4_status = AutoCheckStatus.PASS; * p4_value = "Ej ikryssad"; * } */ checklistItems.Add(new ChecklistItem("P4. Use Gated är korrekt", "Kontrollera att rutan Use Gated under Plan properties svarar mot ordination.", p4_value, p4_status)); if (false)//checklistType == ChecklistType.EclipseGating) { string p5_value = string.Empty; AutoCheckStatus p5_status = AutoCheckStatus.FAIL; if (image != null && image.Comment.ToLower().IndexOf("bh") == -1) { int p5_numberOfPass = 0; foreach (Beam beam in planSetup.Beams) { if (!beam.IsSetupField) { double openMU; double wedgedMU; GetMU(beam, out openMU, out wedgedMU); if (wedgedMU == 0) { p5_numberOfPass++; } } } if (p5_numberOfPass == numberOfTreatmentBeams) { p5_status = AutoCheckStatus.PASS; } p5_value = "EIG"; } else { p5_status = AutoCheckStatus.PASS; p5_value = "Breath hold"; } checklistItems.Add(new ChecklistItem("P5. Kilade fält ej förekommande för EIG", "Kontrollera att det inte finns några kilade fält i EIG gating-plan.", p5_value, p5_status)); } string p6_value = string.Empty; string p6_value_detailed = string.Empty; AutoCheckStatus p6_status = AutoCheckStatus.UNKNOWN; DataTable dataTableTreatmentSessions = AriaInterface.Query("select Session.SessionNum,SessionRTPlan.Status from Session,SessionRTPlan,RTPlan where Session.SessionSer=SessionRTPlan.SessionSer and RTPlan.RTPlanSer=SessionRTPlan.RTPlanSer and RTPlan.PlanSetupSer=" + planSetupSer.ToString() + " order by SessionNum"); foreach (DataRow dataRow in dataTableTreatmentSessions.Rows) { int sessionNr = (int)dataRow[0]; string status = (dataRow[1] == DBNull.Value ? "-" : (string)dataRow[1]); p6_value_detailed += (p6_value_detailed.Length == 0 ? string.Empty : "\r\n") + "Session " + sessionNr.ToString() + ": " + status; } p6_value = "# aktiva sessioner: " + dataTableTreatmentSessions.Rows.Count.ToString(); if (fractionation != null && fractionation.NumberOfFractions > 0 && fractionation.NumberOfFractions == dataTableTreatmentSessions.Rows.Count) { p6_status = AutoCheckStatus.PASS; } else { p6_status = AutoCheckStatus.FAIL; } checklistItems.Add(new ChecklistItem("P6. Behandlingssessionerna är aktiva", "Kontrollera att alla behandlingssessioner är aktiva", p6_value, p6_value_detailed, p6_status)); if (checklistType == ChecklistType.Eclipse || checklistType == ChecklistType.EclipseGating || checklistType == ChecklistType.MasterPlan) { string p7_value = string.Empty; AutoCheckStatus p7_status = AutoCheckStatus.FAIL; int p7_numberOfPass = 0; foreach (Beam beam in planSetup.Beams) { if (!beam.IsSetupField) { double diode_value = double.NaN; if (beam.Comment.Length > 0) { NumberFormatInfo numberFormatInfo = new NumberFormatInfo() { NumberDecimalSeparator = ".", NumberGroupSeparator = string.Empty, NegativeInfinitySymbol = "Inf", PositiveInfinitySymbol = "Inf", NaNSymbol = "NaN", NumberDecimalDigits = 0 }; string[] splitString = beam.Comment.ToLower().Split(new string[] { " ", "gy" }, StringSplitOptions.RemoveEmptyEntries); if (splitString.Length == 0) { double.TryParse(beam.Comment.Replace(',', '.'), NumberStyles.Number | NumberStyles.AllowExponent, numberFormatInfo, out diode_value); } else { double.TryParse(splitString[0].Replace(',', '.'), NumberStyles.Number | NumberStyles.AllowExponent, numberFormatInfo, out diode_value); } } double openMU; double wedgedMU; GetMU(beam, out openMU, out wedgedMU); if (double.IsNaN(diode_value) == false) { p7_numberOfPass++; } else { if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Varian && openMU + wedgedMU < 25) { p7_numberOfPass++; } else if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Elekta && openMU < 25 && openMU + wedgedMU < 50) { p7_numberOfPass++; } } p7_value += (p7_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": " + (double.IsNaN(diode_value) ? "-" : diode_value.ToString() + " Gy"); } } if (p7_numberOfPass == numberOfTreatmentBeams) { p7_status = AutoCheckStatus.PASS; } p7_value = reorderBeamParam(p7_value, ","); checklistItems.Add(new ChecklistItem("P7. Diodvärden finns införda under Comments för fälten", "Kontrollera att diodvärden finns införda för fält med >=25 MU alternativt >=50 MU (öppet+kil) för Elekta.", p7_value, p7_status)); } checklistItems.Add(new ChecklistItem("P8. Dosfördelningen är rimlig", "Kontrollera att dosfördelningen är rimlig med avseende på targettäckning och omkringliggande riskorgan. Kontroll skall göras av varje dosnivå", "", AutoCheckStatus.MANUAL)); string p9_value_detailed = string.Empty; string p9_value = String.Empty; bool isSplit = false; AutoCheckStatus p9_status = AutoCheckStatus.UNKNOWN; if (checklistType == ChecklistType.EclipseVMAT) { isSplit = GetIsSplitVMAT(planSetup); } foreach (Beam beam in planSetup.Beams) { if (!beam.IsSetupField) { double openMU; double wedgedMU; GetMU(beam, out openMU, out wedgedMU); p9_value_detailed += beam.Id + ":\r\n"; if (checklistType == ChecklistType.EclipseVMAT) { p9_value_detailed += " Open: " + openMU.ToString("0.0") + " MU\r\n " + beam.MetersetPerGy.ToString("0.0") + " MU/Gy\r\n"; if (beam.MetersetPerGy > 300 && !isSplit) { p9_value += (p9_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": För många MU/Gy"; } else if (beam.MetersetPerGy > 550 && isSplit) { p9_value += (p9_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": För många MU/Gy"; } } else { p9_value_detailed += " Open: " + openMU.ToString("0.0") + ", Wedged: " + wedgedMU.ToString("0.0") + "\r\n"; } p9_value_detailed += " Energi: " + beam.EnergyModeDisplayName + "\r\n\r\n"; /* * if (openMU < 10 && openMU != 0 || wedgedMU < 30 && wedgedMU != 0) * p9_value += (p9_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": För få MU"; * if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Elekta && openMU + wedgedMU > 999) * p9_value += (p9_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": För många MU"; */ if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Elekta) { if (openMU + wedgedMU > 999) { p9_value += (p9_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": För många MU"; } if (openMU < 10 && openMU != 0 || wedgedMU < 30 && wedgedMU != 0) { p9_value += (p9_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": För få MU"; } } else if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Varian) { if (openMU < 10 && wedgedMU == 0 || wedgedMU != 0 && openMU + wedgedMU < 20) { p9_value += (p9_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": För få MU"; } } } } if (p9_value.Length > 0) { if (checklistType == ChecklistType.EclipseVMAT) { p9_status = AutoCheckStatus.WARNING; } else { p9_status = AutoCheckStatus.FAIL; } p9_value = reorderBeamParam(p9_value, ","); } p9_value_detailed = reorderBeamParam(p9_value_detailed, "\r\n\r\n"); checklistItems.Add(new ChecklistItem("P9. Fälten ser rimliga ut vad gäller form, energi, MU och korrektion av artefakter", "Kontrollera att fälten ser rimliga ut vad gäller form, energi, MU och korrektion av artefakter\r\n • Riktlinje för RapidArc är max 300 MU/Gy om bländarna är utanför target under hela varvet (sett ur BEV). Vid delvis skärmat target är denna gräns max 550 MU/Gy.\r\n • Öppna fält ska ha ≥10 MU.\r\n • Fält med dynamisk kil (Varian) ska ha minst 20 MU.\r\n • Fält med fast kil (Elekta) ska ha ≥30 kilade MU.\r\n • För Elekta gäller dessutom att totala antalet MU per fält (öppet + kilat) ej får överstiga 999 MU.", p9_value, p9_value_detailed, p9_status)); if (checklistType != ChecklistType.EclipseVMAT && checklistType != ChecklistType.EclipseConformal) { if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Elekta) { AutoCheckStatus p10_status = AutoCheckStatus.UNKNOWN; string p10_value = ElektaMLCCheck(planSetup); //p10_status = CheckResult(String.Compare(p10_value, "MLC positioner OK.", true) == 0); if (String.Compare(p10_value, "Fält levererbara.", true) == 0) { p10_status = AutoCheckStatus.PASS; } if (p10_value.IndexOf("ej levererbara") > 0) { p10_status = AutoCheckStatus.FAIL; } else if (p10_value.IndexOf("ej optimala") > 0) { p10_status = AutoCheckStatus.WARNING; } if (p10_status == AutoCheckStatus.WARNING) { var p10_values = p10_value.Split('.').ToList(); p10_values[1] = reorderBeamParam(p10_values[1], ","); p10_value = String.Join <string>(". ", p10_values); } checklistItems.Add(new ChecklistItem("P10. MLC:n är indragen till X-bländare, och ett/två blad är öppna utanför Y-bländare", "Kontrollera att MLC:n är indragen till X-bländare eller innanför, och att ett helt bladpar är öppet utanför Y-bländare på resp. sida om Y1 resp. Y2 har decimal 0,7, 0,8 eller 0,9.", p10_value, p10_status)); } else if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Varian) { AutoCheckStatus p10_status = AutoCheckStatus.UNKNOWN; string p10_value = string.Empty; foreach (Beam beam in planSetup.Beams) { string MLCcheck = VarianMLCCheck(beam); if (MLCcheck.Length > 0) { p10_value += (p10_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": " + VarianMLCCheck(beam); } } if (p10_value.Length > 0) { p10_status = AutoCheckStatus.WARNING; p10_value = reorderBeamParam(p10_value, ","); } else { p10_status = AutoCheckStatus.PASS; } checklistItems.Add(new ChecklistItem("P10. MLC:n är indragen till X-bländare", "Kontrollera att MLC:n är indragen till X-bländare eller innanför", p10_value, p10_status)); } } string p11_value = "Metod: " + planSetup.PlanNormalizationMethod + ", target: " + planSetup.TargetVolumeID + ", prescribed percentage: " + (planSetup.PrescribedPercentage * 100.0).ToString("0.0") + ", värde: " + planSetup.PlanNormalizationValue.ToString("0.0"); AutoCheckStatus p11_status = AutoCheckStatus.MANUAL; if (planSetup.PlanNormalizationMethod.Equals("No plan normalization", StringComparison.OrdinalIgnoreCase)) { p11_status = AutoCheckStatus.FAIL; } double normLimitVMAT = 3.0; if (checklistType == ChecklistType.EclipseVMAT && Math.Abs(planSetup.PlanNormalizationValue - 100) > normLimitVMAT) { p11_status = AutoCheckStatus.FAIL; } if (planSetup.PlanNormalizationMethod.IndexOf("100% in Isocenter of") == -1 && checklistType == ChecklistType.EclipseConformal) { p11_status = AutoCheckStatus.FAIL; } checklistItems.Add(new ChecklistItem("P11. Normering är korrekt", "Kontrollera att planen är normerad på korrekt vis \r\n • Icke-normerade planer accepteras ej. \r\n • Normalt till targetvolymens mediandos (om särskilt skäl föreligger kan en punktnormering användas). \r\n • För stereotaktiska lungor i Eclipse normeras dosen till isocenter och ordineras till 75%-isodosen.\r\n • För stereotaktiska behandlingar av skelettmetastaser ska planen normeras så att 95% av PTV-volymen erhåller ordinerad dos (normal 30 Gy). \r\n • För VMAT ska Plan Normalization Value skall normeringsvärdet vara i intervallet [0.970, 1.030].", p11_value, p11_status)); string p12_value = string.Empty; string p13_value = string.Empty; string p13_value_detailed = string.Empty; AutoCheckStatus p13_status = AutoCheckStatus.WARNING; List <ReferencePoint> referencePoints = new List <ReferencePoint>(); List <double> referencePointDose = new List <double>(); List <double> referencePointTotalDose = new List <double>(); List <int> activeReferencePoints = new List <int>(); // Get dose to reference points in active course foreach (PlanSetup planSetupInCourse in course.PlanSetups) { if (planSetupInCourse.ApprovalStatus != PlanSetupApprovalStatus.UnApproved && planSetupInCourse.ApprovalStatus != PlanSetupApprovalStatus.Rejected) { foreach (Beam beam in planSetupInCourse.Beams) { foreach (FieldReferencePoint fieldReferencePoint in beam.FieldReferencePoints) { int referencePointIndex = -1; for (int refPointNr = 0; refPointNr < referencePoints.Count; refPointNr++) { if (string.Compare(fieldReferencePoint.ReferencePoint.Id, referencePoints[refPointNr].Id) == 0) { referencePointIndex = refPointNr; break; } } if (referencePointIndex == -1) { referencePointIndex = referencePoints.Count; referencePoints.Add(fieldReferencePoint.ReferencePoint); referencePointDose.Add(0.0); referencePointTotalDose.Add(0.0); } referencePointTotalDose[referencePointIndex] += fieldReferencePoint.FieldDose.Dose * (planSetupInCourse.UniqueFractionation == null ? double.NaN : (double)planSetupInCourse.UniqueFractionation.NumberOfFractions); if (planSetupInCourse == planSetup) { referencePointDose[referencePointIndex] += fieldReferencePoint.FieldDose.Dose; if (activeReferencePoints.Contains(referencePointIndex) == false) { activeReferencePoints.Add(referencePointIndex); } } } } } } int p13_numberOfPass = 0; for (int refPointNr = 0; refPointNr < referencePoints.Count; refPointNr++) { p13_value_detailed += (p13_value_detailed.Length == 0 ? string.Empty : "\r\n\r\n") + referencePoints[refPointNr].Id + ":\r\n"; double totalDoseLimit = double.NaN; double dailyDoseLimit = double.NaN; double sessionDoseLimit = double.NaN; DataTable dataTableRefPointLimits = AriaInterface.Query("select distinct RefPoint.RefPointId,RefPoint.TotalDoseLimit,RefPoint.DailyDoseLimit,RefPoint.SessionDoseLimit from PlanSetup,Radiation,RadiationRefPoint,RefPoint where RadiationRefPoint.RefPointSer=RefPoint.RefPointSer and RadiationRefPoint.RadiationSer=Radiation.RadiationSer and PlanSetup.PlanSetupSer=Radiation.PlanSetupSer and PlanSetup.CourseSer=" + courseSer.ToString() + " and RefPoint.RefPointId='" + referencePoints[refPointNr].Id + "'"); if (dataTableRefPointLimits.Rows.Count == 1) { totalDoseLimit = (dataTableRefPointLimits.Rows[0][1] == DBNull.Value ? totalDoseLimit = double.NaN : totalDoseLimit = (double)dataTableRefPointLimits.Rows[0][1]); dailyDoseLimit = (dataTableRefPointLimits.Rows[0][2] == DBNull.Value ? dailyDoseLimit = double.NaN : dailyDoseLimit = (double)dataTableRefPointLimits.Rows[0][2]); sessionDoseLimit = (dataTableRefPointLimits.Rows[0][3] == DBNull.Value ? sessionDoseLimit = double.NaN : sessionDoseLimit = (double)dataTableRefPointLimits.Rows[0][3]); } p13_value_detailed += " Dosbidrag från aktuell plan: " + (fractionation == null ? double.NaN : referencePointDose[refPointNr] * (double)fractionation.NumberOfFractions).ToString("0.000") + " Gy, " + referencePointDose[refPointNr].ToString("0.000") + " Gy/fr " + " (Total dose limit: " + totalDoseLimit.ToString("0.000") + " Gy, daily limit: " + dailyDoseLimit.ToString("0.000") + " Gy, session limit: " + sessionDoseLimit.ToString("0.000") + " Gy)\r\n"; p13_value_detailed += " Totalt dosbidrag från samtliga godkändaplaner: " + referencePointTotalDose[refPointNr].ToString("0.000") + " Gy " + " (Total limit: " + totalDoseLimit.ToString("0.000") + " Gy)"; if (activeReferencePoints.Contains(refPointNr)) // Reference point is present in the active plan { p12_value += (p12_value.Length == 0 ? string.Empty : ", ") + referencePoints[refPointNr].Id + ": " + referencePointDose[refPointNr].ToString("0.000") + " Gy"; p13_value += (p13_value.Length == 0 ? string.Empty : ", ") + referencePoints[refPointNr].Id + ": (T:" + totalDoseLimit.ToString("0.000") + "/D:" + dailyDoseLimit.ToString("0.000") + "/S:" + sessionDoseLimit.ToString("0.000") + " Gy)"; if (Math.Round(referencePointDose[refPointNr], 3) <= Math.Round(dailyDoseLimit, 3) && Math.Round(referencePointDose[refPointNr], 3) <= Math.Round(sessionDoseLimit, 3) && Math.Round(referencePointTotalDose[refPointNr], 3) == Math.Round(totalDoseLimit, 3)) { p13_numberOfPass++; } } } if (activeReferencePoints.Count > 0 && p13_numberOfPass == activeReferencePoints.Count) { p13_status = AutoCheckStatus.MANUAL; } checklistItems.Add(new ChecklistItem("P12. Referenspunkternas dosbidrag är korrekta", "Kontrollera att dosbidrag till referenspunkter (dos) är korrekta:\r\n • Varje plan ska ha en punkt (primary reference point) som summerar upp till ordinerad dos för det största PTV som planen primärt behandlar.\r\n • Om flera planer bidrar med dos till samma targetvolymer eller om en plan bidrar med dos till flera targetvolymer ska det finnas referenspunkter utan lokalisation i alla planer som summerar dosen till dessa volymer.\r\n • Referenspunkterna ska inte ha dosbidrag från tidigare behandlingar.", p12_value, AutoCheckStatus.MANUAL)); checklistItems.Add(new ChecklistItem("P13. Referenspunkternas gränser är korrekta", "Kontrollera att referenspunkternas gränser (dos) är korrekta", p13_value, p13_value_detailed, p13_status)); if (checklistType == ChecklistType.Eclipse || checklistType == ChecklistType.EclipseGating) { checklistItems.Add(new ChecklistItem("P14. Skarven är flyttad korrekt för skarvplan", "Skarvplaner: Skarven är flyttad korrekt och fälten är i övrigt likadana\r\n • Bröstbehandlingar med kollimator i 0° för både huvudfält i fossa- och tang.-fält flyttas endast om eventuellt PTV_66 ligger i skarven.", string.Empty, AutoCheckStatus.MANUAL)); } AutoCheckStatus p15_status = AutoCheckStatus.UNKNOWN; string p15_value = string.Empty; int p15_numberOfPass = 0; List <string> machineId = new List <string>(); foreach (Beam beam in planSetup.Beams) { machineId.Add(beam.TreatmentUnit.Id); if (String.Equals(beam.TreatmentUnit.ToString(), planSetup.Beams.First().TreatmentUnit.ToString())) { p15_numberOfPass += 1; } p15_value += (p15_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": " + beam.TreatmentUnit.Id; } if (p15_numberOfPass == numberOfBeams) { p15_status = AutoCheckStatus.PASS; } else { p15_status = AutoCheckStatus.FAIL; } p15_value = reorderBeamParam(p15_value, ","); checklistItems.Add(new ChecklistItem("P15. Konsekvent maskinval", "Kontrollera att samtliga fält är planerade till en och samma behandlingsapparat.", p15_value, p15_status)); AutoCheckStatus p16_status = AutoCheckStatus.UNKNOWN; string p16_value = string.Empty; string p16_value_detailed = "Följande bokningar är aktiva:\r\n"; string treatMachineIdCommon = machineId.ToArray().GroupBy(v => v) .OrderByDescending(g => g.Count()) .First() .Key; p16_value = "Planerat: " + treatMachineIdCommon + (machineId.Distinct().Count() == 1 ? " (enhetligt)" : " (tvetydigt)"); DateTime date = DateTime.Now; List <string> bookedMachineId = new List <string>(); string bookedMachineIdCommon = string.Empty; //DataTable bookings = AriaInterface.Query("select x.MachineId, x.ScheduledStartTime from (SELECT DISTINCT Patient.PatientSer, Patient.PatientId, ScheduledActivity.ActualEndDate,ScheduledActivity.ScheduledActivityCode, ScheduledActivity.ActivityInstanceSer,Machine.MachineId,ScheduledActivity.ScheduledStartTime, ScheduledActivity.ObjectStatus FROM ScheduledActivity,Attendee,Patient,Machine WHERE Patient.PatientId = " + patient.Id.ToString() + " AND ScheduledActivity.ObjectStatus='Active' AND ScheduledActivity.ScheduledActivityCode = 'Open' AND ScheduledActivity.PatientSer=Patient.PatientSer AND Attendee.ActivityInstanceSer=ScheduledActivity.ActivityInstanceSer AND Machine.ResourceSer=Attendee.ResourceSer and ScheduledActivity.ScheduledStartTime > '" + date.ToString("yyyy-MM-dd") + " 00:00:00') as x where PatientId = '" + patient.Id.ToString() + "' ORDER BY ScheduledStartTime"); DataTable bookings = AriaInterface.Query("SELECT DISTINCT Patient.PatientSer, Patient.PatientId, ScheduledActivity.ActualEndDate,ScheduledActivity.ScheduledActivityCode, ScheduledActivity.ActivityInstanceSer,Machine.MachineId,ScheduledActivity.ScheduledStartTime, ScheduledActivity.ObjectStatus FROM ScheduledActivity,Attendee,Patient,Machine,ActivityInstance,Activity,ActivityCategory WHERE Patient.PatientId = '" + patient.Id.ToString() + "' AND ScheduledActivity.ObjectStatus='Active' AND ScheduledActivity.ScheduledActivityCode = 'Open' AND ScheduledActivity.PatientSer=Patient.PatientSer AND Attendee.ActivityInstanceSer=ScheduledActivity.ActivityInstanceSer AND Attendee.ObjectStatus='Active' AND Machine.MachineType = 'RadiationDevice' AND Machine.ResourceSer=Attendee.ResourceSer AND ActivityInstance.ActivityInstanceSer=ScheduledActivity.ActivityInstanceSer AND Activity.ActivitySer=ActivityInstance.ActivitySer AND ActivityCategory.ActivityCategorySer = Activity.ActivityCategorySer AND ActivityCategory.ActivityCategoryCode = 'Treatment' AND ScheduledActivity.ScheduledStartTime > '" + date.ToString("yyyy-MM-dd") + " 00:00:00' ORDER BY ScheduledStartTime"); if (bookings.Rows.Count > 0) { foreach (DataRow row in bookings.Rows) { bookedMachineId.Add((string)row["MachineId"]); p16_value_detailed += (string)row["ScheduledStartTime"].ToString() + ": " + (string)row["MachineId"] + "\r\n"; } bookedMachineIdCommon = bookedMachineId.ToArray().GroupBy(v => v) .OrderByDescending(g => g.Count()) .First() .Key; p16_value += ", Bokat: " + bookedMachineIdCommon + (bookedMachineId.Distinct().Count() == 1 ? " (enhetligt)" : " (tvetydigt)"); if (String.Equals(treatMachineIdCommon, bookedMachineIdCommon, StringComparison.OrdinalIgnoreCase) && bookedMachineId.Distinct().Count() == 1 && machineId.Distinct().Count() == 1) { p16_status = AutoCheckStatus.PASS; } else if (String.Equals(treatMachineIdCommon, bookedMachineIdCommon, StringComparison.OrdinalIgnoreCase)) { p16_status = AutoCheckStatus.MANUAL; } else { p16_status = AutoCheckStatus.FAIL; } } else { p16_value += ", Bokat: -"; p16_status = AutoCheckStatus.WARNING; } checklistItems.Add(new ChecklistItem("P16. Konsekvens mellan planerad och bokad behandlingsapparat.", "Kontrollera att patienten är bokad till den behandlingsapparat som planen är planerad för.", p16_value, p16_value_detailed, p16_status)); }
public void B() { checklistItems.Add(new ChecklistItem("B. Bolus")); string b1_value = string.Empty; AutoCheckStatus b1_status = AutoCheckStatus.UNKNOWN; if (planSetup.StructureSet != null) { bool tpsBolusExist = false; bool tpsBolusLinkedToField = false; foreach (Structure structure in planSetup.StructureSet.Structures) { if (string.Compare(structure.DicomType, "BOLUS") == 0) { tpsBolusExist = true; } } foreach (Beam b in planSetup.Beams) { if (b.Boluses.Count() > 0) { tpsBolusLinkedToField = true; } } if (!tpsBolusExist) { b1_value = "Ej ansatt"; b1_status = AutoCheckStatus.PASS; } else if (tpsBolusExist && !tpsBolusLinkedToField) { b1_value = "Bolus har ansatts i TPS, men ej kopplats till minst ett fält. Verifera."; b1_status = AutoCheckStatus.WARNING; } else if (tpsBolusExist && tpsBolusLinkedToField) { b1_value = "Bolus har ansatts i TPS, och kopplats till minst ett fält. Verifiera."; } } else { b1_value = "StructureSet saknas"; } checklistItems.Add(new ChecklistItem("B1. I dosplaneringssystemet ansatt bolus är med i beräkningen", "Kontrollera att bolus som ansatts i dosplaneringssystemet är med i beräkningen (kopplat till resp. fält)", b1_value, b1_status)); AutoCheckStatus b2_status = AutoCheckStatus.MANUAL; string b2_value = string.Empty; // Check against prescription if (prescSer > 0) { DataTable bolus = AriaInterface.Query("select PlanSetupSer, PlanSetup.PrescriptionSer, Prescription.PrescriptionSer, BolusFrequency, BolusThickness from PlanSetup, Prescription where PlanSetup.PrescriptionSer = Prescription.PrescriptionSer and PlanSetup.PlanSetupSer = " + planSetupSer.ToString()); if (bolus.Rows.Count > 0) { b2_value += (bolus.Rows[0][3] == DBNull.Value ? string.Empty : (string)bolus.Rows[0][3]); b2_value += (b2_value.Length == 0 ? string.Empty : ", ") + (bolus.Rows[0][4] == DBNull.Value ? string.Empty : (string)bolus.Rows[0][4]); } if (String.IsNullOrWhiteSpace(b2_value)) { b2_value = "Information saknas"; } } else { b2_status = AutoCheckStatus.WARNING; b2_value = "Ordination saknas, kontroll ej möjlig."; } checklistItems.Add(new ChecklistItem("B2. Ordinationen innehåller information om bolus", "Kontrollera att bolus finns angivet i ordinationen (aktuell tjocklek och bolustyp).", b2_value, b2_status)); }
private void GetFieldSizeGridSize(PlanSetup planSetup, double lim, ChecklistType checklistType, out string outText, out AutoCheckStatus checkstatusOut) // Section for controlling field size in all types of plans Lim in mm! { //Note Lim is in mm! //This part checks the size of the fields. outText = string.Empty; checkstatusOut = AutoCheckStatus.MANUAL; string calcSize; int tol = 25; bool isOverTol = false; foreach (Beam beam in planSetup.Beams) { if (!beam.IsSetupField) { var xSz = beam.ControlPoints.Select(i => Math.Abs(i.JawPositions.X2 - i.JawPositions.X1)); var ySz = beam.ControlPoints.Select(i => Math.Abs(i.JawPositions.Y2 - i.JawPositions.Y1)); double percX = 0; double percY = 0; if (xSz.Min() < lim) { percX = (double)xSz.Where(v => v <= lim).Count() / (double)xSz.Count() * 100; } if (ySz.Min() < lim) { percY = (double)ySz.Where(v => v <= lim).Count() / (double)ySz.Count() * 100; } if (percX > 0 || percY > 0) { if (checklistType == ChecklistType.Eclipse || checklistType == ChecklistType.EclipseGating) { outText += (String.IsNullOrEmpty(outText) ? "" : ", ") + beam.Id + ": " + (percX > 0 ? "X-kollimator < " + lim + " mm" : "") + (percY > 0 ? " Y-kollimator < " + lim + " mm" : ""); } else { outText += (String.IsNullOrEmpty(outText) ? "" : ", ") + beam.Id + ": " + (percX > 0 ? "X-kollimator < " + lim + " mm i " + percX.ToString("0.0") + "% av segmenten" : "") + (percY > 0 ? " Y-kollimator < " + lim + " mm i " + percY.ToString("0.0") + "% av segmenten" : ""); } if (percX > tol || percY > tol) { isOverTol = true; } } } } if (!planSetup.PhotonCalculationOptions.TryGetValue("CalculationGridSizeInCM", out calcSize)) { outText = "Ingen beräkningsupplösning " + outText; checkstatusOut = AutoCheckStatus.FAIL; } else { //Ger följande utfall if (calcSize == "0.1" && !String.IsNullOrEmpty(outText)) // outText är ej tom, dvs någon del av något fält < lim mm. Då skall Algoritmen { checkstatusOut = AutoCheckStatus.PASS; } else if (calcSize == "0.25" && String.IsNullOrEmpty(outText)) { checkstatusOut = AutoCheckStatus.PASS; } else if (calcSize == "0.25" && !String.IsNullOrEmpty(outText)) { if (isOverTol) // if more than 25 % of the segments are less than the set value lim this will give a fail, else Warning. { checkstatusOut = AutoCheckStatus.FAIL; } else { checkstatusOut = AutoCheckStatus.WARNING; } } else if (calcSize == "0.1" && String.IsNullOrEmpty(outText)) { checkstatusOut = AutoCheckStatus.WARNING; outText = ", Rekommenderat med 0.25 cm för fält > " + lim / 10 + " cm."; } outText = calcSize + " cm. " + outText; } //return new Tuple<string, AutoCheckStatus>(outText, checkstatusOut); }
public void U() // The name U is kept for historical reasons. Might change to R in future versions. { checklistItems.Add(new ChecklistItem("R. Remiss/Ordination")); string r1_imageid = string.Empty; if (planSetup.StructureSet != null && planSetup.StructureSet.Image != null) { r1_imageid = planSetup.StructureSet.Image.Id; } string r1_value = "Personnummer: " + patient.Id + ", Course: " + course.Id + ", Plan: " + planSetup.Id + ", CT: " + (image == null ? "-" : image.Id); string r1_value_detail = string.Empty; DataTable remarks = AriaInterface.Query("select Image.ImageNotes from Image, Series where Series.SeriesUID = '" + image.Series.UID.ToString() + "' and Image.SeriesSer = Series.SeriesSer and Image.ImageType = 'Image' and Image.ImageId = '" + image.Id.ToString() + "'"); if (remarks.Rows.Count == 1 && remarks.Rows[0][0] != DBNull.Value) { r1_value_detail = (string)remarks.Rows[0][0]; checklistItems.Add(new ChecklistItem("R1. Jämför id (course, plan, CT-set, patient) mellan remiss, protokoll och Aria", "Kontrollera att \r\n • Patientens personnummer stämmer överens mellan remiss och Aria\r\n • Course, plannamn och CT-set stämmer överens mellan protokoll och Aria.", r1_value, r1_value_detail, AutoCheckStatus.MANUAL)); } else { checklistItems.Add(new ChecklistItem("R1. Jämför id (course, plan, CT-set, patient) mellan remiss, protokoll och Aria", "Kontrollera att \r\n • Patientens personnummer stämmer överens mellan remiss och Aria\r\n • Course, plannamn och CT-set stämmer överens mellan protokoll och Aria.", r1_value, AutoCheckStatus.MANUAL)); } AutoCheckStatus r2_status = AutoCheckStatus.FAIL; string r2_value = string.Empty; string r2_value_detail = string.Empty; string prescriptionVolume = string.Empty; long prescriptionAnatomySer = long.MinValue; bool guessedVolume = false; bool multiplePrescriptionLevels = false; string planningVolume = string.Empty; DataTable planning = AriaInterface.Query("select distinct PlanSetupSer, PrimaryPTVSer, PatientVolumeSer, StructureId from PlanSetup, Structure where PlanSetup.PlanSetupSer = " + planSetupSer.ToString() + " and PlanSetup.PrimaryPTVSer = Structure.PatientVolumeSer"); if (planning.Rows.Count == 1 && planning.Rows[0][3] != DBNull.Value) { planningVolume = (string)planning.Rows[0][3]; } DataTable prescription = AriaInterface.Query("select distinct PlanSetupSer, PlanSetup.PrescriptionSer, PrescriptionAnatomy.PrescriptionSer, PrescriptionAnatomy.PrescriptionAnatomySer, PrescriptionAnatomyItem.PrescriptionAnatomySer, ItemType, ItemValue, Prescription.Status, Prescription.PrescriptionSer, Prescription.PrescriptionName, Prescription.Notes from PlanSetup, Prescription, PrescriptionAnatomy, PrescriptionAnatomyItem where PlanSetup.PlanSetupSer = " + planSetupSer.ToString() + " and PlanSetup.PrescriptionSer = PrescriptionAnatomy.PrescriptionSer and PrescriptionAnatomy.PrescriptionAnatomySer = PrescriptionAnatomyItem.PrescriptionAnatomySer and PrescriptionAnatomyItem.ItemType = 'VOLUME ID' and PlanSetup.PrescriptionSer = Prescription.PrescriptionSer"); if (prescription.Rows.Count > 0 && prescription.Rows[0][6] != DBNull.Value) { //string volumeName = string.Empty; string prescriptionStatus = (string)prescription.Rows[0][7]; string prescriptionName = (string)prescription.Rows[0][9]; r2_value_detail = (string)prescription.Rows[0][10]; if (prescription.Rows.Count == 1) { prescriptionVolume = (string)prescription.Rows[0][6]; prescriptionAnatomySer = (long)prescription.Rows[0][3]; } /* * else * { * multiplePrescriptionLevels = true; * foreach (DataRow row in prescription.Rows) * { * string volumeName = (string)row[6]; * if (volumeName.IndexOf(planningVolume) == 0 && planningVolume.Length > 1) * { * prescriptionVolume = (string)row[6]; * prescriptionAnatomySer = (long)row[3]; * guessedVolume = true; * break; * } * } * } */ r2_status = CheckResult(string.Compare(prescriptionStatus, "Approved") == 0); r2_value = prescriptionName + ": " + prescriptionStatus; } else if (prescription.Rows.Count == 0) { r2_value = "Ordination saknas"; } checklistItems.Add(new ChecklistItem("R2. Status på kopplad ordination.", "Kontrollera att planen är kopplad till en ordination kopplad vars status 'Approved'.", r2_value, r2_value_detail, r2_status)); if (r2_status == AutoCheckStatus.PASS) { string r3_value; /* * AutoCheckStatus r3_status = AutoCheckStatus.MANUAL; * if (multiplePrescriptionLevels == true && guessedVolume == false) * { * r3_value = "Multipla ordinationsvolymer existerar. Ingen matchar den planerade volymen. "; * r3_status = AutoCheckStatus.MANUAL; * } * else if (multiplePrescriptionLevels == true && guessedVolume == true) * { * r3_value = "Multipla ordinationsvolymer existerar. Följande matchar den planerade volymen: " + prescriptionVolume + ", "; * r3_status = AutoCheckStatus.MANUAL; * } * else * { * r3_value = "Ordinerad volym: " + prescriptionVolume + ", "; * if (prescriptionVolume.IndexOf(planningVolume) == 0 && planningVolume.Length > 1) // maybe this is too nice. Perhaps it should be a String.Compare. Possibly with a string split for prescritonVolume (using :) * r3_status = AutoCheckStatus.PASS; * else * r3_status = AutoCheckStatus.WARNING; * } * r3_value += "Planerad volym: " + (planningVolume == string.Empty ? "-" : planningVolume); * checklistItems.Add(new ChecklistItem("R3. Kontrollera att ordinerad volym stämmer överens med planerad volym.", "Kontrollera att volymen som planens primära referenspunkt tillhör motsvarar den volym som det är ordinerat till.", r3_value, r3_status)); */ if (prescription.Rows.Count > 1) { r3_value = "Ordinerade volymer: "; } else { r3_value = "Ordinerad volym: "; } foreach (DataRow row in prescription.Rows) { r3_value += (string)row[6] + ", "; } r3_value += "Planerad volym: " + (planningVolume == string.Empty ? "-" : planningVolume); r3_value.Replace(", Planerad volym", "; Planerad volym"); checklistItems.Add(new ChecklistItem("R3. Ordinerad volym stämmer överens med planerad volym.", "Kontrollera att volymen som planens primära referenspunkt tillhör motsvarar den volym som det är ordinerat till.", r3_value, AutoCheckStatus.MANUAL)); AutoCheckStatus r4_status = AutoCheckStatus.UNKNOWN; string r4_value = string.Empty; string r4_value_detailed = string.Empty; if (prescription.Rows.Count == 1) { int numberOfFractions = int.MinValue; double dosePerFraction = double.NaN; double totalDose = double.NaN; DataTable prescriptionItem = AriaInterface.Query("select NumberOfFractions, ItemType, ItemValue, PrescriptionAnatomyItem.PrescriptionAnatomySer, PrescriptionAnatomy.PrescriptionAnatomySer, PrescriptionAnatomy.PrescriptionSer, Prescription.PrescriptionSer from Prescription, PrescriptionAnatomy, PrescriptionAnatomyItem where PrescriptionAnatomy.PrescriptionAnatomySer = " + prescriptionAnatomySer.ToString() + " and PrescriptionAnatomy.PrescriptionAnatomySer = PrescriptionAnatomyItem.PrescriptionAnatomySer and PrescriptionAnatomy.PrescriptionSer = Prescription.PrescriptionSer"); if (prescriptionItem.Rows.Count > 0) { numberOfFractions = (int)prescriptionItem.Rows[0]["NumberOfFractions"]; foreach (DataRow row in prescriptionItem.Rows) { if (String.Equals((string)row["ItemType"], "Total dose", StringComparison.OrdinalIgnoreCase)) { double.TryParse((string)row["ItemValue"], out totalDose); } if (String.Equals((string)row["ItemType"], "Dose per fraction", StringComparison.OrdinalIgnoreCase)) { double.TryParse((string)row["ItemValue"], out dosePerFraction); } } r4_value = "Ordination: " + dosePerFraction.ToString("0.000") + " Gy * " + numberOfFractions.ToString() + " = " + totalDose.ToString("0.000") + " Gy"; r4_value_detailed = "Ordination: \r\n • Volym: " + prescriptionVolume + "\r\n • Fraktionsdos: " + dosePerFraction.ToString("0.000") + " Gy \r\n • Antal fraktioner: " + numberOfFractions.ToString() + "\r\n • Totaldos: " + totalDose.ToString("0.000") + " Gy\r\n"; } if (fractionation != null) { r4_value += (r4_value == null ? "Ordination: - , " : ", ") + "Planerat: " + fractionation.PrescribedDosePerFraction.ToString() + " * " + fractionation.NumberOfFractions.ToString() + " = " + planSetup.TotalPrescribedDose.ToString(); r4_status = CheckResult(numberOfFractions == fractionation.NumberOfFractions && dosePerFraction == fractionation.PrescribedDosePerFraction.Dose && totalDose == planSetup.TotalPrescribedDose.Dose); } } else { r4_status = AutoCheckStatus.MANUAL; if (fractionation != null) { r4_value += (r4_value == string.Empty ? "Ordination: Tvetydigt, " : ", ") + "Planerat: " + fractionation.PrescribedDosePerFraction.ToString() + " * " + fractionation.NumberOfFractions.ToString() + " = " + planSetup.TotalPrescribedDose.ToString(); } if (prescription.Rows.Count > 0) { foreach (DataRow row in prescription.Rows) { string volumeName = (string)row[6]; long ser = (long)row[3]; double totalDose = double.NaN; double dosePerFraction = double.NaN; int nOfFractions = 0; DataTable prescriptionItem = AriaInterface.Query("select NumberOfFractions, ItemType, ItemValue, PrescriptionAnatomyItem.PrescriptionAnatomySer, PrescriptionAnatomy.PrescriptionAnatomySer, PrescriptionAnatomy.PrescriptionSer, Prescription.PrescriptionSer from Prescription, PrescriptionAnatomy, PrescriptionAnatomyItem where PrescriptionAnatomy.PrescriptionAnatomySer = " + ser.ToString() + " and PrescriptionAnatomy.PrescriptionAnatomySer = PrescriptionAnatomyItem.PrescriptionAnatomySer and PrescriptionAnatomy.PrescriptionSer = Prescription.PrescriptionSer"); if (prescriptionItem.Rows.Count > 0) { nOfFractions = (int)prescriptionItem.Rows[0]["NumberOfFractions"]; foreach (DataRow itemRow in prescriptionItem.Rows) { if (String.Equals((string)itemRow["ItemType"], "Total dose", StringComparison.OrdinalIgnoreCase)) { double.TryParse((string)itemRow["ItemValue"], out totalDose); } if (String.Equals((string)itemRow["ItemType"], "Dose per fraction", StringComparison.OrdinalIgnoreCase)) { double.TryParse((string)itemRow["ItemValue"], out dosePerFraction); } } } r4_value_detailed += (r4_value_detailed == string.Empty ? "Ordination: \r\n" : "\r\n") + " • Volym: " + volumeName + "\r\n • Fraktionsdos: " + dosePerFraction.ToString("0.000") + " Gy \r\n • Antal fraktioner: " + nOfFractions.ToString() + "\r\n • Totaldos: " + totalDose.ToString("0.000") + " Gy\r\n"; } } } if (fractionation == null) { r4_status = AutoCheckStatus.FAIL; } else { r4_value_detailed += (r4_value_detailed == string.Empty ? "" : "\r\n") + "Planerat: \r\n • Volym: " + planningVolume + "\r\n • Fraktionsdos: " + fractionation.PrescribedDosePerFraction.ToString() + "\r\n • Antal fraktioner: " + fractionation.NumberOfFractions.ToString() + "\r\n • Totaldos: " + planSetup.TotalPrescribedDose.ToString(); } checklistItems.Add(new ChecklistItem("R4. Planen är konsekvent med vad som ordinerats.", "Kontrollera att planen är konsekvent med vad som ordinerats gällande: \r\n • Fraktionsdos\r\n • Antal fraktioner\r\n • Totaldos", r4_value, r4_value_detailed, r4_status)); } }
public static bool Passed(this AutoCheckStatus checkStatus) { return(checkStatus == AutoCheckStatus.PASS); }
public void D() { checklistItems.Add(new ChecklistItem("D. Dosberäkning")); string d1_value = planSetup.PhotonCalculationModel; if (d1_value.Length == 0) { d1_value = "-"; } AutoCheckStatus d1_status = CheckResult(string.Compare(d1_value, "AAA_13.6.23") == 0); if (checklistType == ChecklistType.MasterPlan || checklistType == ChecklistType.MasterPlanIMRT) { d1_status = AutoCheckStatus.UNKNOWN; checklistItems.Add(new ChecklistItem("D1. Beräkningsalgoritm är korrekt vald", "Kontrollera att korrekt beräkningsalgoritm (PB) har använts vid dosplaneringen.", d1_value, d1_status)); } else { checklistItems.Add(new ChecklistItem("D1. Beräkningsalgoritm är korrekt vald", "Kontrollera att korrekt beräkningsalgoritm (AAA_13.6.26) har använts vid dosplaneringen.", d1_value, d1_status)); } string d2_value; if (!planSetup.PhotonCalculationOptions.TryGetValue("CalculationGridSizeInCM", out d2_value)) { d2_value = "-"; } else { d2_value += " cm"; } AutoCheckStatus d2_status = CheckResult(string.Compare(d2_value, "0.25 cm") == 0); GetFieldSizeGridSize(planSetup, 30, checklistType, out d2_value, out d2_status); //30 är beräsningen satt för kollimatorstorleken. if (checklistType == ChecklistType.MasterPlan || checklistType == ChecklistType.MasterPlanIMRT) { d2_status = AutoCheckStatus.UNKNOWN; checklistItems.Add(new ChecklistItem("D2. Beräkningsupplösningen är korrekt", "Kontrollera att korrekt beräkningsupplösning (0.30 cm) har använts.", d2_value, d2_status)); } else { checklistItems.Add(new ChecklistItem("D2. Beräkningsupplösningen är korrekt", "Kontrollera att korrekt beräkningsupplösning \r\n• 0.10 cm (fält < 3 cm i > 25% av segmenten) eller \r\n• 0.25 cm har använts.", d2_value, d2_status)); } string d3_value; if (!planSetup.PhotonCalculationOptions.TryGetValue("HeterogeneityCorrection", out d3_value)) { d3_value = "-"; } AutoCheckStatus d3_status = CheckResult(string.Compare(d3_value, "ON") == 0); if (checklistType == ChecklistType.MasterPlan || checklistType == ChecklistType.MasterPlanIMRT) { d3_status = AutoCheckStatus.UNKNOWN; } checklistItems.Add(new ChecklistItem("D3. Heterogenitetskorrektion har applicerats korrekt", "Kontrollera att heterogenitetskorrektion har använts om ej särskilda skäl föreligger", d3_value, d3_status)); // VMATFluenceResolution removed from checklist /* * if (checklistType == ChecklistType.EclipseVMAT) * { * string d4_value = string.Empty; * if (!planSetup.PhotonCalculationOptions.TryGetValue("VMATFluenceResolution", out d4_value)) * d4_value = "-"; * AutoCheckStatus d4_status = CheckResult(string.Compare(d4_value, "High") == 0); * checklistItems.Add(new ChecklistItem("D4. Fluensupplösningen är korrekt", "Kontrollera att korrekt fluensupplösning har använts", d4_value, d4_status)); * } */ string d5_value = string.Empty; AutoCheckStatus d5_status = AutoCheckStatus.UNKNOWN; string couchModel = string.Empty; double couchSurfaceHU = double.NaN; double couchInteriorHU = double.NaN; if (structureSet != null) { foreach (Structure structure in structureSet.Structures) { double assignedHU; structure.GetAssignedHU(out assignedHU); if (string.Compare(structure.DicomType, "SUPPORT") == 0) { if (structure.Id.IndexOf("Surface") != -1) { structure.GetAssignedHU(out couchSurfaceHU); couchModel = structure.Name; } if (structure.Id.IndexOf("Interior") != -1) { structure.GetAssignedHU(out couchInteriorHU); } } } } // If a patient have had a support structure connected in a previous course, the name of that structure will be maintained even though it is obsolete. // Specifically: The substring "Top" was part of the name in 13.0, but disappeared in 13.6. Returning patient treated during 13.0 will have that substring retained. // Solution. Replace(" Top".String.Empty) if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Varian && string.Compare(couchModel.Replace(" Top", String.Empty), "Exact IGRT Couch, medium") == 0 && couchInteriorHU == -950 && couchSurfaceHU == -300) { d5_status = AutoCheckStatus.PASS; } else if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Elekta && string.Compare(couchModel.Replace(" Top", String.Empty), "BrainLAB/iBeam Couch") == 0 && couchInteriorHU == -950 && couchSurfaceHU == -300) { d5_status = AutoCheckStatus.PASS; } else if (couchModel.Length == 0) { d5_status = AutoCheckStatus.WARNING; couchModel = "Saknas"; } else { d5_status = AutoCheckStatus.FAIL; } // add on test if plan is non coplanar VMAT if (checklistType == ChecklistType.EclipseVMAT && GetVMATCoplanar(planSetup) == false) { d5_status = AutoCheckStatus.MANUAL; } if (string.Compare(couchModel, "Saknas") == 0) { d5_value = "Saknas (" + treatmentUnitManufacturer + ")"; } else { d5_value = "Model: " + couchModel + ", Interior: " + couchInteriorHU.ToString() + " HU, Surface: " + couchSurfaceHU.ToString() + " HU (" + treatmentUnitManufacturer + ")"; } checklistItems.Add(new ChecklistItem("D5. Britsprofil och HU har valts korrekt", "Kontrollera att korrekt britsprofil och korrekta HU valts under Structure Properties för britsstrukturerna och fliken General samt CT Value and Material.\r\n• Varian: Exact IGRT Couch, medium (CouchSurface: -300, CouchInterior: -950)\r\n• Elekta: BrainLAB/iBeam Couch (CouchSurface: -300, CouchInterior: -950)\r\n• Notera att brits inte ska inkluderas för icke coplanara VMAT behandlingar av intra- och extra-kraniella target kraniellt om C1.", d5_value, d5_status)); // if synthetic CT, compute the distance between body and couch surface if (syntheticCT) { string d6_value = string.Empty; AutoCheckStatus d6_status = AutoCheckStatus.UNKNOWN; double distance = 15.0; double threshold = 5.0; Structure body = structureSet.Structures.Where(s => String.CompareOrdinal(s.Id, "BODY") == 0).ToList().FirstOrDefault(); Structure couchSurface = structureSet.Structures.Where(s => String.CompareOrdinal(s.DicomType, "SUPPORT") == 0).Where(s => s.Id.IndexOf("Surface") != -1).ToList().FirstOrDefault(); if (body == null) { d6_status = AutoCheckStatus.FAIL; d6_value = "Body struktur saknas. "; } if (couchSurface == null) { d6_status = AutoCheckStatus.FAIL; d6_value += "Britsstruktur saknas. "; } if (d6_status != AutoCheckStatus.FAIL) { // compute separation between structures and distance to threshold double separation = 0; if (image.ImagingOrientation.ToString().IndexOf("Prone") > 0) { separation = body.MeshGeometry.Bounds.Location.Y - (couchSurface.MeshGeometry.Bounds.Location.Y + couchSurface.MeshGeometry.Bounds.Size.Y); } else { separation = -1 * (body.MeshGeometry.Bounds.Location.Y + body.MeshGeometry.Bounds.Size.Y - couchSurface.MeshGeometry.Bounds.Location.Y); } if (Math.Round(distance - separation, 0) > threshold) { d6_status = AutoCheckStatus.WARNING; } else { d6_status = AutoCheckStatus.PASS; } d6_value = "Avstånd mellan Body och brits: " + separation.ToString("N0") + " mm"; } checklistItems.Add(new ChecklistItem("D6. Kontrollera avstånd mellan Body och britsstruktur", "Kontrollera att avståndet mellan Body och britsstuktur är [10, 20] mm för syntetisk CT", d6_value, d6_status)); } string d7_value = string.Empty; string d7_value_detailed = string.Empty; AutoCheckStatus d7_status = AutoCheckStatus.UNKNOWN; bool calculationErrorOrWarning = false; foreach (Beam beam in planSetup.Beams) { if (!beam.IsSetupField) { d7_value_detailed += beam.Id + ":\r\n"; foreach (BeamCalculationLog beamCalculationLog in beam.CalculationLogs) { foreach (string messageLine in beamCalculationLog.MessageLines) { if (messageLine.IndexOf("Warning") == 0 || messageLine.IndexOf("Error") == 0) { d7_value_detailed += "• " + messageLine + "\r\n"; calculationErrorOrWarning = true; } } } d7_value_detailed += "\r\n"; } } if (calculationErrorOrWarning == false) { d7_status = AutoCheckStatus.PASS; d7_value = "Inga error eller varningar"; } else { d7_status = AutoCheckStatus.WARNING; d7_value = "Error eller varningar"; } d7_value_detailed = "Fält:\r\n" + reorderBeamParam(d7_value_detailed, "\r\n\r\n"); checklistItems.Add(new ChecklistItem("D7. Eventuella felmeddelanden under Errors And Warnings är acceptabla", "Kontrollera att eventuella meddelanden under Errors And Warnings är acceptabla och bekräfta detta med signatur i protokollet.", d7_value, d7_value_detailed, d7_status)); }
public void U() // The name U is kept for historical reasons. Might change to R in future versions. { checklistItems.Add(new ChecklistItem("R. Strålanmälan/Ordination")); string r1_imageid = string.Empty; if (planSetup.StructureSet != null && planSetup.StructureSet.Image != null) { r1_imageid = planSetup.StructureSet.Image.Id; } string r1_value = "Personnummer: " + patient.Id + ", Course: " + course.Id + ", Plan: " + planSetup.Id + ", CT: " + (image == null ? "-" : image.Id); string r1_value_detail = string.Empty; DataTable remarks = AriaInterface.Query("select Image.ImageNotes from Image, Series where Series.SeriesUID = '" + image.Series.UID.ToString() + "' and Image.SeriesSer = Series.SeriesSer and Image.ImageType = 'Image' and Image.ImageId = '" + image.Id.ToString() + "'"); if (remarks.Rows.Count == 1 && remarks.Rows[0][0] != DBNull.Value) { r1_value_detail = (string)remarks.Rows[0][0]; string remark = (string)remarks.Rows[0][0]; int count = remark.Select((c, i) => remark.Substring(i)).Count(sub => sub.StartsWith("User")); if (count > 1) { MessageBox.Show(remark, "Aktuella remarks"); } } checklistItems.Add(new ChecklistItem("R1. Jämför id (course, plan, CT-set, patient) mellan strålanmälan, protokoll och Aria", "Kontrollera att \r\n • Patientens personnummer stämmer överens mellan strålanmälan, protokoll och Aria\r\n • Course, plannamn och CT-set stämmer överens mellan protokoll och Aria.", r1_value, AutoCheckStatus.MANUAL)); AutoCheckStatus r2_status = AutoCheckStatus.FAIL; string r2_value = string.Empty; string r2_value_detail = string.Empty; string prescriptionVolume = string.Empty; long prescriptionAnatomySer = long.MinValue; bool guessedVolume = false; bool multiplePrescriptionLevels = false; string planningVolume = string.Empty; DataTable prescription = new DataTable(); // If/else based on wether prescription exists or not if (prescSer > 0) // prescription exists { DataTable planning = AriaInterface.Query("select distinct PlanSetupSer, PrimaryPTVSer, PatientVolumeSer, StructureId from PlanSetup, Structure where PlanSetup.PlanSetupSer = " + planSetupSer.ToString() + " and PlanSetup.PrimaryPTVSer = Structure.PatientVolumeSer"); if (planning.Rows.Count == 1 && planning.Rows[0][3] != DBNull.Value) { planningVolume = (string)planning.Rows[0][3]; } prescription = AriaInterface.Query("select distinct PlanSetupSer, PlanSetup.PrescriptionSer, PrescriptionAnatomy.PrescriptionSer, PrescriptionAnatomy.PrescriptionAnatomySer, PrescriptionAnatomyItem.PrescriptionAnatomySer, ItemType, ItemValue, Prescription.Status, Prescription.PrescriptionSer, Prescription.PrescriptionName, Prescription.Notes from PlanSetup, Prescription, PrescriptionAnatomy, PrescriptionAnatomyItem where PlanSetup.PlanSetupSer = " + planSetupSer.ToString() + " and PlanSetup.PrescriptionSer = PrescriptionAnatomy.PrescriptionSer and PrescriptionAnatomy.PrescriptionAnatomySer = PrescriptionAnatomyItem.PrescriptionAnatomySer and PrescriptionAnatomyItem.ItemType = 'VOLUME ID' and PlanSetup.PrescriptionSer = Prescription.PrescriptionSer"); if (prescription.Rows.Count > 0 && prescription.Rows[0][6] != DBNull.Value) { //string volumeName = string.Empty; string prescriptionStatus = (string)prescription.Rows[0][7]; string prescriptionName = (string)prescription.Rows[0][9]; if (prescription.Rows[0][10] != DBNull.Value) { r2_value_detail = (string)prescription.Rows[0][10]; } if (prescription.Rows.Count == 1) { prescriptionVolume = (string)prescription.Rows[0][6]; prescriptionAnatomySer = (long)prescription.Rows[0][3]; } /* * else * { * multiplePrescriptionLevels = true; * foreach (DataRow row in prescription.Rows) * { * string volumeName = (string)row[6]; * if (volumeName.IndexOf(planningVolume) == 0 && planningVolume.Length > 1) * { * prescriptionVolume = (string)row[6]; * prescriptionAnatomySer = (long)row[3]; * guessedVolume = true; * break; * } * } * } */ r2_status = CheckResult(string.Compare(prescriptionStatus, "Approved") == 0); r2_value = prescriptionName + ": " + prescriptionStatus; } else if (prescription.Rows.Count == 0) { r2_value = "Ordination saknas"; } } else // prescription does not exist { r2_value = "Ordination saknas/är inte kopplad"; //r2_value_detail = "Kontrollen kan ej utföras korrekt utan kopplad ordination."; r2_status = AutoCheckStatus.WARNING; } if (String.IsNullOrEmpty(r2_value_detail)) { checklistItems.Add(new ChecklistItem("R2. Status på kopplad ordination.", "Kontrollera att planen är kopplad till en ordination med status 'Approved'.", r2_value, r2_status)); } else { checklistItems.Add(new ChecklistItem("R2. Status på kopplad ordination.", "Kontrollera att planen är kopplad till en ordination med status 'Approved'.", r2_value, r2_value_detail, r2_status)); } string r3_value = String.Empty; /* * AutoCheckStatus r3_status = AutoCheckStatus.MANUAL; * if (multiplePrescriptionLevels == true && guessedVolume == false) * { * r3_value = "Multipla ordinationsvolymer existerar. Ingen matchar den planerade volymen. "; * r3_status = AutoCheckStatus.MANUAL; * } * else if (multiplePrescriptionLevels == true && guessedVolume == true) * { * r3_value = "Multipla ordinationsvolymer existerar. Följande matchar den planerade volymen: " + prescriptionVolume + ", "; * r3_status = AutoCheckStatus.MANUAL; * } * else * { * r3_value = "Ordinerad volym: " + prescriptionVolume + ", "; * if (prescriptionVolume.IndexOf(planningVolume) == 0 && planningVolume.Length > 1) // maybe this is too nice. Perhaps it should be a String.Compare. Possibly with a string split for prescritonVolume (using :) * r3_status = AutoCheckStatus.PASS; * else * r3_status = AutoCheckStatus.WARNING; * } * r3_value += "Planerad volym: " + (planningVolume == string.Empty ? "-" : planningVolume); * checklistItems.Add(new ChecklistItem("R3. Kontrollera att ordinerad volym stämmer överens med planerad volym.", "Kontrollera att volymen som planens primära referenspunkt tillhör motsvarar den volym som det är ordinerat till.", r3_value, r3_status)); */ switch (prescription.Rows.Count) { case 0: r3_value += "Ordination saknas. "; break; case 1: r3_value += "Ordinerad volym: "; break; default: r3_value += "Ordinerade volymer: "; break; } if (prescription.Rows.Count > 1) { r3_value = "Ordinerade volymer: "; } else { r3_value = "Ordinerad volym: "; } foreach (DataRow row in prescription.Rows) { r3_value += (string)row[6] + ", "; } r3_value += "Planerad volym: " + (planningVolume == string.Empty ? "-" : planningVolume); r3_value = r3_value.Replace(", Planerad volym", "; Planerad volym"); checklistItems.Add(new ChecklistItem("R3. Ordinerad volym stämmer överens med planerad volym.", "Kontrollera att volymen som planens primära referenspunkt tillhör motsvarar den volym som det är ordinerat till.", r3_value, AutoCheckStatus.MANUAL)); AutoCheckStatus r4_status = AutoCheckStatus.UNKNOWN; string r4_value = string.Empty; string r4_value_detailed = string.Empty; List <int> numberOfFractions = new List <int>(); List <double> dosePerFraction = new List <double>(); List <double> totalDose = new List <double>(); switch (prescription.Rows.Count) { case 0: r4_status = AutoCheckStatus.WARNING; r4_value = "Ordination: Saknas"; break; default: foreach (DataRow row in prescription.Rows) { string volumeName = (string)row[6]; long ser = (long)row[3]; DataTable prescriptionItem = AriaInterface.Query("select NumberOfFractions, ItemType, ItemValue, PrescriptionAnatomyItem.PrescriptionAnatomySer, PrescriptionAnatomy.PrescriptionAnatomySer, PrescriptionAnatomy.PrescriptionSer, Prescription.PrescriptionSer from Prescription, PrescriptionAnatomy, PrescriptionAnatomyItem where PrescriptionAnatomy.PrescriptionAnatomySer = " + ser.ToString() + " and PrescriptionAnatomy.PrescriptionAnatomySer = PrescriptionAnatomyItem.PrescriptionAnatomySer and PrescriptionAnatomy.PrescriptionSer = Prescription.PrescriptionSer"); double tdose = -1, dosepf = -1; foreach (DataRow itemRow in prescriptionItem.Rows) { numberOfFractions.Add((int)prescriptionItem.Rows[0]["NumberOfFractions"]); if (String.Equals((string)itemRow["ItemType"], "Total dose", StringComparison.OrdinalIgnoreCase)) { double.TryParse((string)itemRow["ItemValue"], out tdose); totalDose.Add(tdose); } if (String.Equals((string)itemRow["ItemType"], "Dose per fraction", StringComparison.OrdinalIgnoreCase)) { double.TryParse((string)itemRow["ItemValue"], out dosepf); dosePerFraction.Add(dosepf); } } if (tdose > 0 && dosepf > 0) { r4_value_detailed += (r4_value_detailed == string.Empty ? "Ordination: \r\n" : "\r\n") + " • Volym: " + volumeName + "\r\n • Fraktionsdos: " + dosepf.ToString("0.000") + " Gy \r\n • Antal fraktioner: " + numberOfFractions.LastOrDefault().ToString() + "\r\n • Totaldos: " + tdose.ToString("0.000") + " Gy\r\n"; } } // Check if numberOfFractions, dosePerFraction are distinct if (numberOfFractions.Distinct().ToList().Count > 1) { r4_status = AutoCheckStatus.FAIL; r4_value = "Ordination: Inkonsekvent antal fraktioner"; } else { if (dosePerFraction.Distinct().ToList().Count > 1) { r4_status = AutoCheckStatus.MANUAL; r4_value = "Ordination: SIB * " + numberOfFractions[0].ToString(); } if (numberOfFractions.Distinct().ToList().Count == 1 && dosePerFraction.Distinct().ToList().Count == 1) { r4_value = "Ordination: " + dosePerFraction[0].ToString("0.000") + "Gy * " + numberOfFractions[0].ToString() + " = " + totalDose[0].ToString("0.000") + " Gy"; } } break; } if (fractionation == null) { r4_status = AutoCheckStatus.FAIL; } else { if (r4_status == AutoCheckStatus.UNKNOWN) { r4_status = CheckResult(numberOfFractions[0] == fractionation.NumberOfFractions && Math.Round(dosePerFraction[0], 3) == Math.Round(fractionation.PrescribedDosePerFraction.Dose, 3) && Math.Round(totalDose[0], 3) == Math.Round(planSetup.TotalPrescribedDose.Dose, 3)); } // Even for a SIB we need to check the number of fractions if (r4_status == AutoCheckStatus.MANUAL) { if (numberOfFractions[0] != fractionation.NumberOfFractions) { r4_status = AutoCheckStatus.FAIL; } } r4_value += "; Planerat: " + fractionation.PrescribedDosePerFraction.ToString() + " * " + fractionation.NumberOfFractions.ToString() + " = " + planSetup.TotalPrescribedDose.ToString(); r4_value_detailed += (r4_value_detailed == string.Empty ? "" : "\r\n") + "Planerat: \r\n • Volym: " + planningVolume + "\r\n • Fraktionsdos: " + fractionation.PrescribedDosePerFraction.ToString() + "\r\n • Antal fraktioner: " + fractionation.NumberOfFractions.ToString() + "\r\n • Totaldos: " + planSetup.TotalPrescribedDose.ToString(); } checklistItems.Add(new ChecklistItem("R4. Planen är konsekvent med vad som ordinerats.", "Kontrollera att planen är konsekvent med vad som ordinerats gällande: \r\n • Fraktionsdos\r\n • Antal fraktioner\r\n • Totaldos", r4_value, r4_value_detailed, r4_status)); }
public void V() { if (checklistType == ChecklistType.EclipseVMAT || checklistType == ChecklistType.MasterPlanIMRT) { checklistItems.Add(new ChecklistItem("V. VMAT/IMRT")); string v1_value = string.Empty; AutoCheckStatus v1_status = AutoCheckStatus.FAIL; int v1_numberOfWarnings = 0; int v1_numberOfPass = 0; List <double> collAngles = new List <double>(); foreach (Beam beam in planSetup.Beams) { if (!beam.IsSetupField) { double collimatorAngle = beam.ControlPoints[0].CollimatorAngle; collAngles.Add(collimatorAngle); if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Varian) { if (collimatorAngle == 5 || collimatorAngle == 355) { v1_numberOfPass++; } else if (collimatorAngle > 5 && collimatorAngle < 355) { v1_numberOfWarnings++; } } else if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Elekta) { if (collimatorAngle == 30 || collimatorAngle == 330) { v1_numberOfPass++; } } v1_value += (v1_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": " + collimatorAngle.ToString("0.0") + "°"; } } if (v1_numberOfPass == numberOfTreatmentBeams) { v1_status = AutoCheckStatus.PASS; } else if (v1_numberOfPass + v1_numberOfWarnings == numberOfTreatmentBeams) { v1_status = AutoCheckStatus.WARNING; } if (collAngles.Count > 1 && collAngles.Distinct().ToList().Count < 2) { v1_status = AutoCheckStatus.FAIL; } checklistItems.Add(new ChecklistItem("V1. Kollimatorvinkeln är lämplig", "Kontrollera att kollimatorvinkeln är lämplig\r\n • Varian: vanligtvis 5° resp. 355°, men passar detta ej PTV är andra vinklar ok (dock ej vinklar mellan 355° och 5°)\r\n • Elekta: 30° resp. 330°", v1_value, v1_status)); if (checklistType == ChecklistType.EclipseVMAT)//JSR && treatmentUnitManufacturer == TreatmentUnitManufacturer.Varian) { string v2_value = string.Empty; AutoCheckStatus v2_status = AutoCheckStatus.WARNING; int v2_numberOfPass = 0; foreach (Beam beam in planSetup.Beams) { if (!beam.IsSetupField) { double fieldWidth = 0.1 * (beam.ControlPoints[0].JawPositions.X2 - beam.ControlPoints[0].JawPositions.X1); if (fieldWidth <= 15) { v2_numberOfPass++; } v2_value += (v2_value.Length == 0 ? string.Empty : ", ") + beam.Id + ": " + fieldWidth.ToString("0.0") + " cm"; } } if (v2_numberOfPass == numberOfTreatmentBeams) { v2_status = AutoCheckStatus.PASS; } checklistItems.Add(new ChecklistItem("V2. Fältbredden är rimlig ", "Kontrollera att VMAT-fält har en rimlig fältbredd (riktvärde 15 cm, vid större target ska två arcs och delade fält övervägas).", v2_value, v2_status)); string v3_details = string.Empty; string v3_value = string.Empty; AutoCheckStatus v3_status = AutoCheckStatus.MANUAL; checklistItems.Add(new ChecklistItem("V3. Optimeringsbolus är korrekt använt", "Kontrollera att optimeringsbolus har använts korrekt för ytliga target: \r\n Eclipse H&N (VMAT):\r\n • Optimeringsbolus har använts vid optimeringen i de fall då PTV ligger mindre än 4 mm innanför ytterkonturen.\r\n • BODY ska inkludera eventuellt optimeringsbolus\r\n Eclipse Ani, Recti (VMAT):\r\n • BODY ska inkludera eventuellt optimeringsbolus\r\n Optimeringsbolus i Eclipse (VMAT):\r\n • HU för optimeringsbolus är satt till 0 HU\r\n • Optimeringsbolus är skapat genom 5 mm (H&N) eller 6 mm (Ani, Recti) expansion från det PTV-struktur optimeringen skett på. Boluset ska ej gå innanför patientens hudyta.", v3_value, v3_details, v3_status)); //JSR checklistItems.Add(new ChecklistItem("V4. Robusthet", "Kontrollera planens robusthet m.a.p. ISO-center-förskjutning m.h.a. Uncertainty-planer. Planerna skapas av dosplaneraren.\r\n • Skillnaderna i maxdos för uncertainty-planerna (±0,4 cm i x, y, resp. z) är <5% relativt originalplanen.\r\n • CTV täckning är acceptabel.", string.Empty, AutoCheckStatus.MANUAL)); // Check for jawtracking: var v5_values = CheckJawTracking(planSetup); string v5_value = v5_values.Item1; AutoCheckStatus v5_status = v5_values.Item2; checklistItems.Add(new ChecklistItem("V5. Leveransmönstret är rimligt", "Kontrollera att leveransmönstret är rimligt (att det inte är en stor andel extremt små öppningar, att riskorgan skärmas, att alla segment går på ett target samt jawtracking för kollimatorerna)", v5_value, v5_status)); } } }
public void I() { checklistItems.Add(new ChecklistItem("I. Bildmaterial")); string i1_value = string.Empty; AutoCheckStatus i1_status; if (image != null && image.Series != null) { if (string.Compare(image.Series.ImagingDeviceId, "CT_A") == 0 || string.Compare(image.Series.ImagingDeviceId, "CT_B") == 0 || string.Compare(image.Series.ImagingDeviceId, "CT_C") == 0 || string.Compare(image.Series.ImagingDeviceId, "PET/CT 01") == 0 || string.Compare(image.Series.ImagingDeviceId, "PET/CT 02") == 0 || string.Compare(image.Series.ImagingDeviceId, "PET/CT 03") == 0) { if (image.Comment.IndexOf("RT") == 0) { i1_status = AutoCheckStatus.PASS; } else { i1_status = AutoCheckStatus.FAIL; } i1_value = image.Series.ImagingDeviceId + ", " + image.Comment; } // Specific checks for synthetic CTs else if (string.Compare(image.Series.ImagingDeviceId, "sCT_MR_A") == 0) { // set synthetic CT syntheticCT = true; string trigger = "MR acquisition: "; i1_status = AutoCheckStatus.MANUAL; //string MRparameterCheckFile = patient.LastName + "_" + image.Series.Study.CreationDateTime.Value.ToString("yyyyMMdd") + "_" + image.Series.Study.CreationDateTime.Value.ToString("HHmmss") + ".txt"; string MRparameterCheckFile = patient.LastName + "_" + image.Comment.Substring(image.Comment.IndexOf(trigger) + trigger.Length).Trim() + ".txt"; string MRparameterCheckResult = ValidateMR(MRparameterCheckFile); if (!String.Equals("MRI SYNTHETIC CT PARAMETERS OK", MRparameterCheckResult)) { i1_status = AutoCheckStatus.FAIL; } i1_value = image.Series.ImagingDeviceId + ", " + MRparameterCheckResult + ", " + image.Comment; } else { i1_status = AutoCheckStatus.FAIL; i1_value = image.Series.ImagingDeviceId + ", " + image.Comment; } } else { i1_status = AutoCheckStatus.FAIL; i1_value = "-"; } if (checklistType == ChecklistType.MasterPlan || checklistType == ChecklistType.MasterPlanIMRT) { i1_status = AutoCheckStatus.UNKNOWN; } checklistItems.Add(new ChecklistItem("I1. CT-protokoll för radioterapi har använts", "Kontrollera att ett CT-protokoll för radioterapi har använts:\r\n• Eclipse: Se Image comment under Image properties (för serien) eller protokollet", i1_value, i1_status)); if (syntheticCT == true) { string sCT_version = "v1.1.2"; // The version is first in the ImageComment of each slice. Select the first slice in the Image (the first item is the volume, so skip that). Split the string on whitespace and take the first item string i2_value = image.Series.Images.Where(img => img.ZSize == 1).FirstOrDefault().Comment.ToString().Split(' ').FirstOrDefault().ToString(); AutoCheckStatus i2_status = CheckResult(string.Compare(i2_value, sCT_version) == 0); checklistItems.Add(new ChecklistItem("I2. MRI-planner version", "Kontrollera att korrekt version av MRI-planner använts vid generering av sCT\r\n• " + sCT_version, i2_value, i2_status)); } // Will now use information from Prescription rather than verifying against ChecklistType string i3_value = "CT-underlag: "; AutoCheckStatus i3_status = AutoCheckStatus.MANUAL; if (image != null) { i3_value += image.Id + "; Ordination: "; } else { i3_value += "-" + "; Ordination: "; i3_status = AutoCheckStatus.FAIL; } DataTable prescription = AriaInterface.Query("select Gating from Prescription, PlanSetup where PlanSetup.PrescriptionSer = Prescription.PrescriptionSer and PlanSetup.PlanSetupSer = '" + planSetupSer.ToString() + "'"); switch (prescription.Rows.Count) { case 0: if (i3_status != AutoCheckStatus.FAIL) { i3_status = AutoCheckStatus.WARNING; } i3_value = "Ordination saknas"; break; case 1: if (prescription.Rows[0]["Gating"] != DBNull.Value) { i3_value += (string)prescription.Rows[0]["Gating"]; } else { i3_value += "Friandning"; } break; default: i3_value += "Obestämbart"; break; } /* * if (image != null) * { * if (checklistType == ChecklistType.EclipseGating) * { * if (image.Comment.ToLower().IndexOf("gating") != -1 && image.Id.ToLower().IndexOf("gating") != -1 || image.Comment.ToLower().IndexOf("bh") != -1 && image.Id.ToLower().IndexOf("bh") != -1) * i3_status = AutoCheckStatus.PASS; * else * i3_status = AutoCheckStatus.FAIL; * } * else * { * if (image.Comment.ToLower().IndexOf("gating") != -1 || image.Id.ToLower().IndexOf("gating") != -1 || image.Comment.ToLower().IndexOf("bh") != -1 || image.Id.ToLower().IndexOf("bh") != -1) * i3_status = AutoCheckStatus.FAIL; * else * i3_status = AutoCheckStatus.PASS; * } * i3_value = image.Comment + ", " + image.Id; * } * else * { * i3_status = AutoCheckStatus.FAIL; * i3_value = "-"; * } * if (checklistType == ChecklistType.MasterPlan || checklistType == ChecklistType.MasterPlanIMRT) * i3_status = AutoCheckStatus.UNKNOWN; */ checklistItems.Add(new ChecklistItem("I3. CT-studie är korrekt m.a.p. gatingordination", "Kontrollera att den korrekta CT-studien med avseende på gatingordination har använts.", i3_value, i3_status)); string i4_value = string.Empty; AutoCheckStatus i4_status = AutoCheckStatus.UNKNOWN; if (structureSet != null) { int firstImagePlane = int.MaxValue; int lastImagePlane = int.MinValue; foreach (Structure structure in structureSet.Structures) { if (string.Compare(structure.DicomType, "PTV") == 0) { for (int imagePlane = 0; imagePlane < image.ZSize; imagePlane++) { VVector[][] contour = structure.GetContoursOnImagePlane(imagePlane); if (contour.Length > 0 && contour[0].Length > 0) { if (imagePlane < firstImagePlane) { firstImagePlane = imagePlane; } if (imagePlane > lastImagePlane) { lastImagePlane = imagePlane; } } } } } if (firstImagePlane != int.MaxValue && lastImagePlane != int.MinValue) { double minusZ = 0.1 * firstImagePlane * image.ZRes; double plusZ = 0.1 * (image.ZSize - lastImagePlane - 1) * image.ZRes; if (minusZ >= 4 && plusZ >= 4) { i4_status = AutoCheckStatus.PASS; } else { i4_status = AutoCheckStatus.WARNING; } // DICOM -> IEC61217: z -> y i4_value = "-y: " + minusZ.ToString("0.0") + " cm, +y: " + plusZ.ToString("0.0") + " cm"; } } checklistItems.Add(new ChecklistItem("I4. Axiell utökning av beräkningsvolym gjorts då det behövs", "Kontrollera att axiell utökning av beräkningsvolym gjorts då det behövs (<4 cm mellan target och första/sista snittet i 3D-volymen) och även att det inte gjorts då det är oberättigat (t.ex. superior för skalle).", i4_value, i4_status)); string i5_value = (image == null ? "-" : image.ImagingOrientation.ToString()); checklistItems.Add(new ChecklistItem("I5. Patientriktning har angivits korrekt vid CT-undersökningen", "Kontrollera att patientriktning har angivits korrekt vid CT-undersökningen genom att jämföra orienteringsfigur mot CT-data.", i5_value, AutoCheckStatus.MANUAL)); AutoCheckStatus i6_status = AutoCheckStatus.MANUAL; string i6_value = ("Planorientering: " + planSetup.TreatmentOrientation.ToString()); i6_value += "; CT-orientering: " + i5_value; if (!String.Equals(planSetup.TreatmentOrientation.ToString(), image.ImagingOrientation.ToString())) { i6_status = AutoCheckStatus.WARNING; } checklistItems.Add(new ChecklistItem("I6. Orientering är konsekvent mellan CT-undersökning och behandlingsplan.", "Kontrollera att samma orientering valts för CT-undersökning och behandlingsplanen om inte särskilda skäl föreligger.", i6_value, i6_status)); string i7_value = (checklistType == ChecklistType.EclipseGating ? "Obs Gating! Referenspunkt sätts utifrån icke-gatad CT" : string.Empty); checklistItems.Add(new ChecklistItem("I7. Referenspunkten (anatomisk) är korrekt placerad", "Kontrollera att referenspunkten (anatomisk) är korrekt placerad (User Origin i Eclipse). Observera att på patienter som ska ha gating sätts User Origin utifrån det icke gatade CT-underlaget.", i7_value, AutoCheckStatus.MANUAL)); }
public ChecklistItem(string shortInfo, string detailedInfo, string shortResult, string detailedResult, AutoCheckStatus autoCheckStatus) { this.shortInfo = shortInfo; this.detailedInfo = detailedInfo; this.shortResult = shortResult; this.detailedResult = detailedResult; this.autoCheckStatus = autoCheckStatus.ToString(); }
public void I() { checklistItems.Add(new ChecklistItem("I. Bildmaterial")); string i1_value = string.Empty; AutoCheckStatus i1_status; if (image != null && image.Series != null) { if (string.Compare(image.Series.ImagingDeviceId, "CT_A") == 0 || string.Compare(image.Series.ImagingDeviceId, "CT_B") == 0 || string.Compare(image.Series.ImagingDeviceId, "CT_C") == 0 || string.Compare(image.Series.ImagingDeviceId, "PET/CT 01") == 0) { if (image.Comment.IndexOf("RT") == 0) { i1_status = AutoCheckStatus.PASS; } else { i1_status = AutoCheckStatus.FAIL; } } else { i1_status = AutoCheckStatus.FAIL; } i1_value = image.Series.ImagingDeviceId + ", " + image.Comment; } else { i1_status = AutoCheckStatus.FAIL; i1_value = "-"; } if (checklistType == ChecklistType.MasterPlan || checklistType == ChecklistType.MasterPlanIMRT) { i1_status = AutoCheckStatus.UNKNOWN; } checklistItems.Add(new ChecklistItem("I1. CT-protokoll för radioterapi har använts", "Kontrollera att ett CT-protokoll för radioterapi har använts:\r\n• Eclipse: Se Image comment under Image properties (för serien) eller protokollet", i1_value, i1_status)); // consider using information from prescription if we can get prescriptions sorted string i2_value = string.Empty; AutoCheckStatus i2_status; if (image != null) { if (checklistType == ChecklistType.EclipseGating) { if (image.Comment.ToLower().IndexOf("gating") != -1 && image.Id.ToLower().IndexOf("gating") != -1 || image.Comment.ToLower().IndexOf("bh") != -1 && image.Id.ToLower().IndexOf("bh") != -1) { i2_status = AutoCheckStatus.PASS; } else { i2_status = AutoCheckStatus.FAIL; } } else { if (image.Comment.ToLower().IndexOf("gating") != -1 || image.Id.ToLower().IndexOf("gating") != -1 || image.Comment.ToLower().IndexOf("bh") != -1 || image.Id.ToLower().IndexOf("bh") != -1) { i2_status = AutoCheckStatus.FAIL; } else { i2_status = AutoCheckStatus.PASS; } } i2_value = image.Comment + ", " + image.Id; } else { i2_status = AutoCheckStatus.FAIL; i2_value = "-"; } if (checklistType == ChecklistType.MasterPlan || checklistType == ChecklistType.MasterPlanIMRT) { i2_status = AutoCheckStatus.UNKNOWN; } checklistItems.Add(new ChecklistItem("I2. CT-studie är korrekt m.a.p. gatingordination", "Kontrollera att den korrekta CT-studien med avseende på gatingordination har använts.", i2_value, i2_status)); string i3_value = string.Empty; AutoCheckStatus i3_status = AutoCheckStatus.UNKNOWN; if (structureSet != null) { int firstImagePlane = int.MaxValue; int lastImagePlane = int.MinValue; foreach (Structure structure in structureSet.Structures) { if (string.Compare(structure.DicomType, "PTV") == 0) { for (int imagePlane = 0; imagePlane < image.ZSize; imagePlane++) { VVector[][] contour = structure.GetContoursOnImagePlane(imagePlane); if (contour.Length > 0 && contour[0].Length > 0) { if (imagePlane < firstImagePlane) { firstImagePlane = imagePlane; } if (imagePlane > lastImagePlane) { lastImagePlane = imagePlane; } } } } } if (firstImagePlane != int.MaxValue && lastImagePlane != int.MinValue) { double minusZ = 0.1 * firstImagePlane * image.ZRes; double plusZ = 0.1 * (image.ZSize - lastImagePlane - 1) * image.ZRes; if (minusZ >= 4 && plusZ >= 4) { i3_status = AutoCheckStatus.PASS; } else { i3_status = AutoCheckStatus.WARNING; } // DICOM -> IEC61217: z -> y i3_value = "-y: " + minusZ.ToString("0.0") + " cm, +y: " + plusZ.ToString("0.0") + " cm"; } } checklistItems.Add(new ChecklistItem("I3. Axiell utökning av beräkningsvolym gjorts då det behövs", "Kontrollera att axiell utökning av beräkningsvolym gjorts då det behövs (<4 cm mellan target och första/sista snittet i 3D-volymen) och även att det inte gjorts då det är oberättigat (t.ex. superior för skalle).", i3_value, i3_status)); string i4_value = (image == null ? "-" : image.ImagingOrientation.ToString()); checklistItems.Add(new ChecklistItem("I4. Patientriktning har angivits korrekt vid CT-undersökningen", "Kontrollera att patientriktning har angivits korrekt vid CT-undersökningen genom att jämföra orienteringsfigur mot CT-data.", i4_value, AutoCheckStatus.MANUAL)); AutoCheckStatus i5_status = AutoCheckStatus.MANUAL; string i5_value = ("Planorientering: " + planSetup.TreatmentOrientation.ToString()); i5_value += "; CT-orientering: " + i4_value; if (!String.Equals(planSetup.TreatmentOrientation.ToString(), image.ImagingOrientation.ToString())) { i5_status = AutoCheckStatus.WARNING; } checklistItems.Add(new ChecklistItem("I5. Orientering är konsekvent mellan CT-undersökning och behandlingsplan.", "Kontrollera att samma orientering valts för CT-undersökning och behandlingsplanen om inte särskilda skäl föreligger.", i5_value, i5_status)); string i6_value = (checklistType == ChecklistType.EclipseGating ? "Obs Gating! Referenspunkt sätts utifrån icke-gatad CT" : string.Empty); checklistItems.Add(new ChecklistItem("I6. Referenspunkten (anatomisk) är korrekt placerad", "Kontrollera att referenspunkten (anatomisk) är korrekt placerad (User Origin i Eclipse). Observera att på patienter som ska ha gating sätts User Origin utifrån det icke gatade CT-underlaget.", i6_value, AutoCheckStatus.MANUAL)); }
public void X() { checklistItems.Add(new ChecklistItem("X. Slutförande")); //checklistItems.Add(new ChecklistItem("X1. Skriv in antal MU, diodvärden och följande eventuella diodkorrektioner i behandlingskortet", "Skriv in antal MU, diodvärden och följande eventuella diodkorrektioner i behandlingskortet (se ”In vivo-dosimetri”-dokumentet för detaljer):\r\n • Kil (Mimatordioder på Elekta)\r\n • Kort SSD (IBA-dioder)\r\n • Långt SSD (Mimatordioder)", string.Empty, AutoCheckStatus.MANUAL)); string x2_value = string.Empty; AutoCheckStatus x2_status = AutoCheckStatus.MANUAL; if (image != null && image.Series != null) { x2_value = image.Series.ImagingDeviceId; if (string.Compare(image.Series.ImagingDeviceId, "CT_A") == 0 || string.Compare(image.Series.ImagingDeviceId, "CT_B") == 0 || string.Compare(image.Series.ImagingDeviceId, "CT_C") == 0) { if (planSetup.Beams.Count() > 0) { double userY = -image.UserOrigin.y * 0.1; double isoY = -(planSetup.Beams.First().IsocenterPosition.y - image.UserOrigin.y) * 0.1; if (planSetup.TreatmentOrientation.ToString().IndexOf("Prone") != -1) // Change sign if Orientation is Prone. { isoY *= -1; userY *= -1; } double shiftY = 7.1; double sumY = -isoY - userY + shiftY; x2_value += ", Beräknad britshöjd: " + (-userY).ToString("0.0") + (-isoY >= 0 ? "+" : string.Empty) + (-isoY).ToString("0.0") + "+" + shiftY.ToString("0.0") + " = " + sumY.ToString("0.0") + " cm"; if (sumY < -30) { x2_status = AutoCheckStatus.WARNING; } } } else if (string.Compare(image.Series.ImagingDeviceId, "PET/CT 01") == 0) { x2_value += ", Mät position manuellt"; } } checklistItems.Add(new ChecklistItem("X2. Förväntad britshöjd räknas ut och läggs in i Aria", "Räkna ut förväntad britshöjd och lägg in i Aria (på alla fält, inklusive setup-fält) i modulen Treatment Preparation i rutan för Couch Vrt:\r\n• Eclipse: -DICOM offset Z - isocenter Z + offset cm\r\n• Offset är 7,1 cm för CT_A, CT_B, CT_C och -17,5 för PET/CT 01 (kan dock variera beroende på britshöjd vid PET-undersökningen)\r\n• Observera att vid för prone byter DICOM-koordinaten tecken\r\n• Observera risk för kollision mellan gantry och bord vid Vrt < -30 cm\r\nVid SSD-teknik:\r\n • Ska tjockleken på eventuell vacuumpåse bestämmas genom mätning i CT-bilderna och antecknas under Setup note\r\n • Räkna ut förflyttning från fältet närmast 0° till övriga fält (Isocenterkoordinat för ursprungsfältet minus övriga fälts isocenterkoordinater) och anteckna detta på sida 2 i behandlingsprotokollet. Exempel: Relativ förflyttning från fält 1 till fält 2: ∆Vrt=25,0 cm.\r\n • Skriv följande under Setup note: ”FHA-beh. Ring fysiker vid start.”", x2_value, x2_status)); //checklistItems.Add(new ChecklistItem("X2. Förväntad britshöjd räknas ut och läggs in i Aria", "Räkna ut förväntad britshöjd och lägg in i Aria (på alla fält, inklusive setup-fält) i modulen Treatment Preparation i rutan för Couch Vrt:\r\n• Eclipse: -DICOM offset Z - isocenter Z + offset cm\r\nMasterPlan: -TPRP coordinate Z - isocenter Z + offset cm\r\n• Offset är 7,1 cm för CT_A, CT_B, CT_C och -17,5 för PET/CT 01 (kan dock variera beroende på britshöjd vid PET-undersökningen)\r\n• Observera risk för kollision mellan gantry och bord vid Vrt < -30 cm\r\nVid SSD-teknik:\r\n • Ska tjockleken på eventuell vacuumpåse bestämmas genom mätning i CT-bilderna och antecknas under Setup note\r\n • Räkna ut förflyttning från fältet närmast 0° till övriga fält (Isocenterkoordinat för ursprungsfältet minus övriga fälts isocenterkoordinater) och anteckna detta på sida 2 i behandlingsprotokollet. Exempel: Relativ förflyttning från fält 1 till fält 2: ∆Vrt=25,0 cm.\r\n • Skriv följande under Setup note: ”FHA-beh. Ring fysiker vid start.”", x2_value, x2_status)); // Add elinores corda computation here if (checklistType == ChecklistType.EclipseVMAT && GetVMATCoplanar(planSetup) == false) { checklistItems.Add(new ChecklistItem("X3. Notera icke coplanar VMAT under Setup note", "Planen i fråga är en icke coplanar VMAT behandling. Säkerställ att en notering om detta finns under planens Setup note", string.Empty, AutoCheckStatus.MANUAL)); } if (checklistType == ChecklistType.Eclipse || checklistType == ChecklistType.EclipseGating) { checklistItems.Add(new ChecklistItem("X4. Genomför oberoende MU-kontroll", "Genomför obeorende MU-kontroll via RVP", "", AutoCheckStatus.MANUAL)); } if (checklistType == ChecklistType.EclipseVMAT) { checklistItems.Add(new ChecklistItem("X5. QC Course sätts till Completed.", "Sätt status på QC coursen till Completed.", "", AutoCheckStatus.MANUAL)); } checklistItems.Add(new ChecklistItem("X6. Treatment Approved", "Gör planen Treatment Approved. Planen får endast göras Treatment Approved efter att ovanstående kontroller är utförda och Oberoende MU-kontroll eller QC-mätning är godkänd.", string.Empty, AutoCheckStatus.MANUAL)); checklistItems.Add(new ChecklistItem("X7. Task sätts till Done", "Tryck Done när alla kontroller är klara\r\n • Ändra Qty till det antal planer som har kontrollerats\r\n • Om planen har kontrollmätts tycker man Done först när planen både är kontrollerad och kontrollmätt", string.Empty, AutoCheckStatus.MANUAL)); //checklistItems.Add(new ChecklistItem("X5. Signera i rutan Fysiker kontroll", "Genomgången checklista med accepterat resultat bekräftas med signatur i behandlingskortet i rutan Fysiker kontroll.", string.Empty, AutoCheckStatus.MANUAL)); }
public void D() { checklistItems.Add(new ChecklistItem("D. Dosberäkning")); string d1_value = planSetup.PhotonCalculationModel; if (d1_value.Length == 0) { d1_value = "-"; } AutoCheckStatus d1_status = CheckResult(string.Compare(d1_value, "AAA_13.6.23") == 0); if (checklistType == ChecklistType.MasterPlan || checklistType == ChecklistType.MasterPlanIMRT) { d1_status = AutoCheckStatus.UNKNOWN; checklistItems.Add(new ChecklistItem("D1. Beräkningsalgoritm är korrekt vald", "Kontrollera att korrekt beräkningsalgoritm (PB) har använts vid dosplaneringen.", d1_value, d1_status)); } else { checklistItems.Add(new ChecklistItem("D1. Beräkningsalgoritm är korrekt vald", "Kontrollera att korrekt beräkningsalgoritm (AAA_13.0.26) har använts vid dosplaneringen.", d1_value, d1_status)); } string d2_value; if (!planSetup.PhotonCalculationOptions.TryGetValue("CalculationGridSizeInCM", out d2_value)) { d2_value = "-"; } else { d2_value += " cm"; } AutoCheckStatus d2_status = CheckResult(string.Compare(d2_value, "0.25 cm") == 0); if (checklistType == ChecklistType.MasterPlan || checklistType == ChecklistType.MasterPlanIMRT) { d2_status = AutoCheckStatus.UNKNOWN; checklistItems.Add(new ChecklistItem("D2. Beräkningsupplösningen är korrekt", "Kontrollera att korrekt beräkningsupplösning (0.30 cm) har använts.", d2_value, d2_status)); } else { checklistItems.Add(new ChecklistItem("D2. Beräkningsupplösningen är korrekt", "Kontrollera att korrekt beräkningsupplösning (0.25 cm) har använts.", d2_value, d2_status)); } string d3_value; if (!planSetup.PhotonCalculationOptions.TryGetValue("HeterogeneityCorrection", out d3_value)) { d3_value = "-"; } AutoCheckStatus d3_status = CheckResult(string.Compare(d3_value, "ON") == 0); if (checklistType == ChecklistType.MasterPlan || checklistType == ChecklistType.MasterPlanIMRT) { d3_status = AutoCheckStatus.UNKNOWN; } checklistItems.Add(new ChecklistItem("D3. Heterogenitetskorrektion har applicerats korrekt", "Kontrollera att heterogenitetskorrektionen har använts om ej särskilda skäl föreligger", d3_value, d3_status)); // VMATFluenceResolution removed from checklist /* * if (checklistType == ChecklistType.EclipseVMAT) * { * string d4_value = string.Empty; * if (!planSetup.PhotonCalculationOptions.TryGetValue("VMATFluenceResolution", out d4_value)) * d4_value = "-"; * AutoCheckStatus d4_status = CheckResult(string.Compare(d4_value, "High") == 0); * checklistItems.Add(new ChecklistItem("D4. Fluensupplösningen är korrekt", "Kontrollera att korrekt fluensupplösning har använts", d4_value, d4_status)); * } */ string d5_value = string.Empty; AutoCheckStatus d5_status = AutoCheckStatus.UNKNOWN; string couchModel = string.Empty; double couchSurfaceHU = double.NaN; double couchInteriorHU = double.NaN; if (structureSet != null) { foreach (Structure structure in structureSet.Structures) { double assignedHU; structure.GetAssignedHU(out assignedHU); if (string.Compare(structure.DicomType, "SUPPORT") == 0) { if (structure.Id.IndexOf("Surface") != -1) { structure.GetAssignedHU(out couchSurfaceHU); couchModel = structure.Name; } if (structure.Id.IndexOf("Interior") != -1) { structure.GetAssignedHU(out couchInteriorHU); } } } } if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Varian && string.Compare(couchModel, "Exact IGRT Couch, medium") == 0 && couchInteriorHU == -950 && couchSurfaceHU == -300) { d5_status = AutoCheckStatus.PASS; } else if (treatmentUnitManufacturer == TreatmentUnitManufacturer.Elekta && string.Compare(couchModel, "BrainLAB/iBeam Couch") == 0 && couchInteriorHU == -950 && couchSurfaceHU == -300) { d5_status = AutoCheckStatus.PASS; } else if (couchModel.Length == 0) { d5_status = AutoCheckStatus.WARNING; couchModel = "Saknas"; } else { d5_status = AutoCheckStatus.FAIL; } // add on test if plan is non coplanar VMAT if (checklistType == ChecklistType.EclipseVMAT && GetVMATCoplanar(planSetup) == false) { d5_status = CheckResult(string.Compare(couchModel, "Saknas") == 0); } if (string.Compare(couchModel, "Saknas") == 0) { d5_value = "Saknas (" + treatmentUnitManufacturer + ")"; } else { d5_value = "Model: " + couchModel + ", Interior: " + couchInteriorHU.ToString() + " HU, Surface: " + couchSurfaceHU.ToString() + " HU (" + treatmentUnitManufacturer + ")"; } checklistItems.Add(new ChecklistItem("D5. Britsprofil och HU har valts korrekt", "Kontrollera att korrekt britsprofil och korrekta HU valts under Structure Properties för britsstrukturerna och fliken General samt CT Value and Material.\r\n• Varian: Exact IGRT Couch, medium (CouchSurface: -300, CouchInterior: -950)\r\n• Elekta: BrainLAB/iBeam Couch (CouchSurface: -300, CouchInterior: -950)\r\n• Notera att brits inte ska inkluderas för icke coplanara VMAT behandlingar", d5_value, d5_status)); string d6_value = string.Empty; string d6_value_detailed = string.Empty; AutoCheckStatus d6_status = AutoCheckStatus.UNKNOWN; bool calculationErrorOrWarning = false; foreach (Beam beam in planSetup.Beams) { if (!beam.IsSetupField) { d6_value_detailed += beam.Id + ":\r\n"; foreach (BeamCalculationLog beamCalculationLog in beam.CalculationLogs) { foreach (string messageLine in beamCalculationLog.MessageLines) { if (messageLine.IndexOf("Warning") == 0 || messageLine.IndexOf("Error") == 0) { d6_value_detailed += "• " + messageLine + "\r\n"; calculationErrorOrWarning = true; } } } d6_value_detailed += "\r\n"; } } if (calculationErrorOrWarning == false) { d6_status = AutoCheckStatus.PASS; d6_value = "Inga error eller varningar"; } else { d6_status = AutoCheckStatus.WARNING; d6_value = "Error eller varningar"; } d6_value_detailed = "Fält:\r\n" + reorderBeamParam(d6_value_detailed, "\r\n\r\n"); checklistItems.Add(new ChecklistItem("D6. Eventuella felmeddelanden under Errors And Warnings är acceptabla", "Kontrollera att eventuella meddelanden under Errors And Warnings är acceptabla och bekräfta detta med signatur i protokollet.", d6_value, d6_value_detailed, d6_status)); }