LogikalServer.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. using InABox.Integration.Logikal;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Threading.Tasks;
  7. using Ofcas.Lk.Api.Client.Core;
  8. using Ofcas.Lk.Api.Client.Ui;
  9. using Ofcas.Lk.Api.Shared;
  10. namespace PRSLogikal
  11. {
  12. public class LogikalLogArguments
  13. {
  14. public String Message { get; private set; }
  15. public LogikalLogArguments(string message)
  16. {
  17. Message = message;
  18. }
  19. }
  20. public delegate void LogikalLogEvent(object sender, LogikalLogArguments args);
  21. public class LogikalServer : IDisposable
  22. {
  23. public event LogikalLogEvent Log;
  24. private void DoLog(String message) => Log?.Invoke(this, new LogikalLogArguments(message));
  25. private IServiceProxyUiResult _proxy;
  26. private ICoreObjectResult<ILoginScopeUi> _login;
  27. public IntPtr WindowHandle { get; private set; }
  28. private static readonly LogikalErrorResponse NOTCONNECTED = new LogikalErrorResponse()
  29. {
  30. Status = LogikalStatus.Disconnected,
  31. Message = $"LogiKal is not connected"
  32. };
  33. private static readonly LogikalErrorResponse NOTLOGGEDIN = new LogikalErrorResponse()
  34. {
  35. Status = LogikalStatus.NotLoggedIn,
  36. Message = $"Not Logged In"
  37. };
  38. public LogikalServer(IntPtr windowHandle)
  39. {
  40. WindowHandle = windowHandle;
  41. }
  42. public LogikalResponse Connect(LogikalConnectRequest request)
  43. {
  44. if (_proxy != null)
  45. return new LogikalConnectResponse();
  46. var _p = ServiceProxyUiFactory.CreateServiceProxy(request.Path, "ERP");
  47. var _status = _p.ServiceProxyUi.Start();
  48. if (_status.OperationCode != OperationCode.Accepted)
  49. {
  50. return new LogikalErrorResponse()
  51. {
  52. Status = LogikalStatus.CannotConnect,
  53. Message = $"Unable to connect to LogiKal at [{request.Path}]: {_status}"
  54. };
  55. }
  56. _proxy = _p;
  57. return new LogikalConnectResponse();
  58. }
  59. public LogikalResponse Disconnect()
  60. {
  61. if (_login != null)
  62. Logout();
  63. if (_proxy != null)
  64. {
  65. _proxy.ServiceProxyUi.Stop();
  66. _proxy.Dispose();
  67. }
  68. _proxy = null;
  69. return new LogikalDisconnectResponse();
  70. }
  71. private void DoOnDisconnecting()
  72. {
  73. }
  74. public LogikalResponse Login(LogikalLoginRequest request)
  75. {
  76. Dictionary<string, object> _parameters = new Dictionary<string, object>()
  77. {
  78. { WellKnownParameterKey.Login.ProgramMode, "erp" },
  79. { WellKnownParameterKey.Login.ApplicationHandle, WindowHandle },
  80. //{ WellKnownParameterKey.Login.UserName, username },
  81. //{ WellKnownParameterKey.Login.Password, password },
  82. { WellKnownParameterKey.Login.EnableEventSynchronization, true },
  83. };
  84. if (_proxy == null)
  85. return NOTCONNECTED;
  86. if (_login != null)
  87. return new LogikalLoginResponse();
  88. var _check = _proxy.ServiceProxyUi.CanLogin(_parameters);
  89. if (!_check.CanExecute)
  90. {
  91. return new LogikalErrorResponse()
  92. {
  93. Status = LogikalStatus.Restricted,
  94. Message = $"Login not allowed: {_check}!"
  95. };
  96. }
  97. try
  98. {
  99. var _l = _proxy.ServiceProxyUi.Login(_parameters);
  100. if (_l.OperationCode != OperationCode.Accepted)
  101. {
  102. _login = null;
  103. return new LogikalErrorResponse()
  104. {
  105. Status = LogikalStatus.Failed,
  106. Message = $"Login failed: {_l}"
  107. };
  108. }
  109. else
  110. {
  111. _login = _l;
  112. return new LogikalLoginResponse();
  113. }
  114. }
  115. catch (Exception e)
  116. {
  117. return new LogikalErrorResponse()
  118. {
  119. Status = LogikalStatus.Error,
  120. Message = $"{e.Message}\n{e.StackTrace}"
  121. };
  122. }
  123. }
  124. public LogikalResponse Logout()
  125. {
  126. if (_login != null)
  127. _login.Dispose();
  128. _login = null;
  129. return new LogikalLogoutResponse();
  130. }
  131. public bool IsLoggedIn() => _login != null;
  132. private void GetProjectCentres(ICoreObjectResult<IProjectCenterUi> center, List<LogikalProjectCentre> results)
  133. {
  134. var _projectresults = new List<LogikalProject>();
  135. IList<IBaseProjectInfo> _projects = center.CoreObject.ChildrenInfos;
  136. foreach (var _project in _projects)
  137. {
  138. var _summary = new LogikalProject();
  139. PopulateProject(_project, _summary);
  140. _projectresults.Add(_summary);
  141. }
  142. var _result = new LogikalProjectCentre()
  143. {
  144. ID = center.CoreObject.ProjectCenterContainer.Id,
  145. Name = center.CoreObject.Parent != null ? center.CoreObject.Info.DirectoryName : "Project Center",
  146. ParentID = center.CoreObject.Parent != null
  147. ? center.CoreObject.Parent.ProjectCenterContainer.Id
  148. : Guid.Empty,
  149. Projects = _projectresults.ToArray()
  150. };
  151. results.Add(_result);
  152. var _children = center.CoreObject.ProjectCenterContainer.GetChildren().CoreObjectResults;
  153. foreach (var _child in _children)
  154. GetProjectCentres(_child, results);
  155. }
  156. private void PopulateProject(IBaseProjectInfo source, ILogikalProject target)
  157. {
  158. target.ID = source.Guid;
  159. target.Title = source.Name;
  160. target.PersonInCharge = source.PersonInCharge;
  161. target.Path = source.Path;
  162. target.LastUpdated = source.LastChangedDateTime;
  163. target.Created = source.CreatedDateTime;
  164. target.JobNumber = source.AsProjectInfo().JobNumber;
  165. target.OfferNumber = source.AsProjectInfo().OfferNumber;
  166. }
  167. private List<LogikalProjectCentre> GetProjectCentres()
  168. {
  169. var _results = new List<LogikalProjectCentre>();
  170. var _info = _login.CoreObject.ProjectCenterInfos.FirstOrDefault(x => x.Type.Id == 0);
  171. if (_info != null)
  172. {
  173. var _center = _login.CoreObject.GetProjectCenter(_info);
  174. GetProjectCentres(_center, _results);
  175. }
  176. return _results;
  177. }
  178. public LogikalResponse GetProjectCentres(LogikalProjectCentresRequest request)
  179. {
  180. if (_proxy == null)
  181. return NOTCONNECTED;
  182. if (_login == null)
  183. return NOTLOGGEDIN;
  184. var _results = GetProjectCentres();
  185. return new LogikalProjectCentresResponse<LogikalProjectCentre,LogikalProject>() { ProjectCentres = _results.ToArray() };
  186. }
  187. public LogikalResponse GetProjects(LogikalProjectsRequest request)
  188. {
  189. if (_proxy == null)
  190. return NOTCONNECTED;
  191. if (_login == null)
  192. return NOTLOGGEDIN;
  193. List<LogikalProject> _results = new List<LogikalProject>();
  194. var _centres = GetProjectCentres();
  195. foreach (var _centre in _centres)
  196. {
  197. var _projects = _centre.Projects.Where(x => string.IsNullOrWhiteSpace(request.JobNumber) || string.Equals(x.JobNumber, request.JobNumber));
  198. _results.AddRange(_projects);
  199. }
  200. return new LogikalProjectsResponse<LogikalProject>() { Projects = _results.ToArray() };
  201. }
  202. public LogikalResponse GetProject(LogikalProjectRequest request)
  203. {
  204. if (_proxy == null)
  205. return NOTCONNECTED;
  206. if (_login == null)
  207. return NOTLOGGEDIN;
  208. var _project = _login.CoreObject.GetProjectByGuid(request.ProjectID);
  209. if (_project == null)
  210. return new LogikalErrorResponse()
  211. {
  212. Status = LogikalStatus.InvalidProjectID,
  213. Message = $"Cannot Load Project {request.ProjectID}"
  214. };
  215. var response = new LogikalProjectResponse<LogikalProject>();
  216. PopulateProject(_project.CoreObject.Info, response.Project);
  217. return response;
  218. }
  219. public LogikalResponse GetPhases(LogikalPhasesRequest request)
  220. {
  221. if (_proxy == null)
  222. return NOTCONNECTED;
  223. if (_login == null)
  224. return NOTLOGGEDIN;
  225. var _project = _login.CoreObject.GetProjectByGuid(request.ProjectID);
  226. if (_project == null)
  227. return new LogikalErrorResponse()
  228. {
  229. Status = LogikalStatus.InvalidProjectID,
  230. Message = $"Cannot Load Project {request.ProjectID}"
  231. };
  232. List<LogikalPhase> _results = new List<LogikalPhase>();
  233. var _phases = _project.CoreObject.GetChildren().CoreObjectResults;
  234. foreach (ICoreObjectResult<IPhase> _phase in _phases)
  235. {
  236. var _result = new LogikalPhase()
  237. {
  238. ID = _phase.CoreObject.Info.Name,
  239. Title = string.IsNullOrWhiteSpace(_phase.CoreObject.Info.Name) ? "Default Phase" : _phase.CoreObject.Info.Name
  240. };
  241. _results.Add(_result);
  242. }
  243. return new LogikalPhasesResponse<LogikalPhase>() { Phases = _results.ToArray() };
  244. }
  245. public LogikalResponse GetElevationSummaries(LogikalElevationSummaryRequest request)
  246. {
  247. if (_proxy == null)
  248. return NOTCONNECTED;
  249. if (_login == null)
  250. return NOTLOGGEDIN;
  251. var _results = new List<LogikalElevationSummary>();
  252. var _project = _login.CoreObject.GetProjectByGuid(request.ProjectID);
  253. if (_project == null)
  254. return new LogikalErrorResponse()
  255. {
  256. Status = LogikalStatus.InvalidProjectID,
  257. Message = $"Cannot Load Project {request.ProjectID}"
  258. };
  259. var _phases = _project.CoreObject.GetChildren().CoreObjectResults;
  260. var _phase = _phases.FirstOrDefault(x => x.CoreObject.Info.Name == request.Phase);
  261. if (_phase == null)
  262. return new LogikalErrorResponse()
  263. {
  264. Status = LogikalStatus.InvalidPhaseID,
  265. Message = $"Cannot find phase [{request.Phase}] within project [{request.ProjectID}]"
  266. };
  267. var _elevations = _phase.CoreObject.GetChildren().CoreObjectResults;
  268. foreach (var _elevation in _elevations)
  269. {
  270. if (!_elevation.CoreObject.Info.IsInRecycleBin)
  271. {
  272. var _result = new LogikalElevationSummary();
  273. PopulateElevation(_elevation.CoreObject, _result);
  274. _results.Add(_result);
  275. }
  276. }
  277. return new LogikalElevationSummaryResponse<LogikalElevationSummary>() { Elevations = _results.ToArray() };
  278. }
  279. private void PopulateElevation(IElevation source, ILogikalElevationSummary target)
  280. {
  281. target.ID = source.Info.Guid;
  282. target.Name = source.Info.Name;
  283. target.Description = source.Info.SystemDescription;
  284. target.Size = source.Info.Size;
  285. using (var ms = new MemoryStream())
  286. {
  287. IStreamResult thumbnail =
  288. source.GetThumbnail(new Dictionary<string, object>() { });
  289. thumbnail.Stream.CopyTo(ms);
  290. target.Thumbnail = ms.GetBuffer();
  291. }
  292. }
  293. public LogikalResponse GetBillOfMaterials(LogikalBOMRequest request)
  294. {
  295. if (_proxy == null)
  296. return NOTCONNECTED;
  297. if (_login == null)
  298. return NOTLOGGEDIN;
  299. var _project = _login.CoreObject.GetProjectByGuid(request.ProjectID);
  300. if (_project == null)
  301. return new LogikalErrorResponse()
  302. {
  303. Status = LogikalStatus.InvalidProjectID,
  304. Message = $"Cannot Load Project [{request.ProjectID}]"
  305. };
  306. var _elevations = new List<IElevationInfo>();
  307. if (request.ElevationIDs?.Any() == true)
  308. {
  309. var _phases = _project.CoreObject.GetChildren();
  310. foreach (var _phase in _phases.CoreObjectResults)
  311. _elevations.AddRange(_phase.CoreObject.ChildrenInfos.Where(x => request.ElevationIDs.Contains(x.Guid)));
  312. }
  313. using (IReportItemsResult reportItemsResult = _project.CoreObject.GetReports())
  314. {
  315. if (reportItemsResult.OperationCode != OperationCode.Accepted)
  316. {
  317. return new LogikalErrorResponse()
  318. {
  319. Status = LogikalStatus.Error,
  320. Message = $"Cannot Get Reports for Project!"
  321. };
  322. }
  323. // Filter available reports for the erp export report item
  324. IReportItem reportItem = reportItemsResult.ReportItems.First(rep =>
  325. (rep.Id == WellKnownReports.Delivery.ErpExport) &&
  326. (rep.Category.Id == WellKnownReports.Delivery.CategoryId));
  327. // Create parameters for erp export, export format is required, but always sqlite
  328. var exportParameters = new Dictionary<string, object>
  329. {
  330. { WellKnownParameterKey.Project.Report.ExportFormat, "SQLite" },
  331. };
  332. // Check if report can be exported for the given parameters
  333. var operationInfo = _project.CoreObject.CanGetReport(reportItem, _elevations, exportParameters);
  334. if (!operationInfo.CanExecute)
  335. {
  336. return new LogikalErrorResponse()
  337. {
  338. Status = LogikalStatus.Error,
  339. Message = $"Cannot Get Erp Report for Project!"
  340. };
  341. }
  342. // Run report creation asynchronously - begin method starts the operation in background task
  343. using (ISynchronizedOperationResult synchronizedOperationResult =
  344. _project.CoreObject.BeginGetReport(reportItem, _elevations, exportParameters))
  345. {
  346. var response = new LogikalBOMResponse<LogikalBOM, LogikalFinish, LogikalProfile, LogikalGasket, LogikalComponent, LogikalGlass, LogikalLabour>();
  347. // End method waits for the background operation to complete in separate task
  348. using (IStreamResult partsResult = Task.Run<IStreamResult>(() =>
  349. _project.CoreObject.EndGetReport(synchronizedOperationResult.SynchronizedOperation)).Result)
  350. {
  351. Stream exportStream = partsResult.Stream;
  352. using (var _ms = new MemoryStream())
  353. {
  354. exportStream.CopyTo(_ms);
  355. response.BOM.SQLiteData = _ms.GetBuffer();
  356. }
  357. }
  358. return response;
  359. }
  360. }
  361. }
  362. public LogikalResponse GetElevationDetails(LogikalElevationDetailRequest detailRequest)
  363. {
  364. var _project = _login.CoreObject.GetProjectByGuid(detailRequest.ProjectID);
  365. if (_project == null)
  366. return new LogikalErrorResponse()
  367. {
  368. Status = LogikalStatus.InvalidProjectID,
  369. Message = $"Cannot Load Project [{detailRequest.ProjectID}]"
  370. };
  371. Dictionary<Guid, ICoreObjectResult<IElevationUi>> elevations = new Dictionary<Guid, ICoreObjectResult<IElevationUi>>();
  372. var _phases = _project.CoreObject.GetChildren().CoreObjectResults;
  373. foreach (var id in detailRequest.IDs)
  374. {
  375. foreach (var _phase in _phases)
  376. {
  377. var ui = _phase.CoreObject.GetChildren().CoreObjectResults
  378. .FirstOrDefault(e => e.CoreObject.Info.Guid == id);
  379. if (ui != null)
  380. elevations[id] = ui;
  381. }
  382. if (!elevations.ContainsKey(id))
  383. {
  384. return new LogikalErrorResponse()
  385. {
  386. Status = LogikalStatus.ElevationNotFound,
  387. Message = $"Cannot find Elevation [{id}] within project [{detailRequest.ProjectID}]"
  388. };
  389. }
  390. }
  391. try
  392. {
  393. List<LogikalElevationDetail> results = new List<LogikalElevationDetail>();
  394. var response = new LogikalElevationDetailResponse<LogikalElevationDetail, LogikalFinish, LogikalProfile, LogikalGasket, LogikalComponent, LogikalGlass, LogikalLabour>();
  395. foreach (var id in elevations.Keys)
  396. {
  397. var _elevation = elevations[id];
  398. LogikalElevationDetail newel = new LogikalElevationDetail();
  399. PopulateElevation(_elevation.CoreObject, newel);
  400. var dxf = ElevationDetails(detailRequest, _elevation, id, newel, detailRequest.DrawingFormat, detailRequest.DrawingView, detailRequest.DrawingType);
  401. if (dxf != null)
  402. return dxf;
  403. results.Add(newel);
  404. }
  405. response.Elevations = results;
  406. return response;
  407. }
  408. catch (Exception e)
  409. {
  410. return new LogikalErrorResponse() { Status = LogikalStatus.Error, Message = $"{e.Message}\n\n{e.StackTrace}" };
  411. }
  412. }
  413. private LogikalResponse ElevationDetails(LogikalElevationDetailRequest detailRequest, ICoreObjectResult<IElevationUi> _elevation, Guid id,
  414. LogikalElevationDetail newel, LogikalDrawingFormat format, LogikalDrawingView view, LogikalDrawingType type)
  415. {
  416. // Setup parameters for export of the elevation drawing
  417. var sectionDrawingParameters = new Dictionary<string, object>
  418. {
  419. { WellKnownParameterKey.Elevation.Drawing.Format, format == LogikalDrawingFormat.DXF ? ElevationDrawingFormat.DXF : ElevationDrawingFormat.PNG },
  420. { WellKnownParameterKey.Elevation.Drawing.View, view == LogikalDrawingView.Exterior ? View.Exterior : View.Interior },
  421. { WellKnownParameterKey.Elevation.Drawing.Type, type == LogikalDrawingType.Explosion
  422. ? ElevationDrawingType.Explosion
  423. : type == LogikalDrawingType.Section
  424. ? ElevationDrawingType.Section
  425. : type == LogikalDrawingType.Elevation
  426. ? ElevationDrawingType.Elevation
  427. : type == LogikalDrawingType.ElevationWithSectionLines
  428. ? ElevationDrawingType.ElevationWithSectionLines
  429. : ElevationDrawingType.SectionLine
  430. },
  431. { WellKnownParameterKey.Elevation.Drawing.DxfVersion, DxfVersion.Acad2013 },
  432. { WellKnownParameterKey.Elevation.Drawing.ShowDescription, true },
  433. { WellKnownParameterKey.Elevation.Drawing.ShowDimensions, true },
  434. { WellKnownParameterKey.Elevation.Drawing.Scale, 1.0 },
  435. };
  436. // Check if the drawing can be exported for the elevation with the given parameters
  437. if (!_elevation.CoreObject.CanGetDrawing(sectionDrawingParameters).CanExecute)
  438. {
  439. return new LogikalErrorResponse()
  440. {
  441. Status = LogikalStatus.Error,
  442. Message = $"GetDrawing() not permitted for Elevation [{id}]"
  443. };
  444. }
  445. // Generate drawing for the elevation with the given parameters
  446. using (IDrawingResult drawingResult = _elevation.CoreObject.GetDrawing(sectionDrawingParameters))
  447. {
  448. Stream exportStream = drawingResult.Stream;
  449. using (var _ms = new MemoryStream())
  450. {
  451. exportStream.CopyTo(_ms);
  452. newel.Drawing = _ms.GetBuffer();
  453. }
  454. }
  455. using (IStreamResult partsResult = _elevation.CoreObject.GetPartsList())
  456. {
  457. Stream exportStream = partsResult.Stream;
  458. using (var _ms = new MemoryStream())
  459. {
  460. exportStream.CopyTo(_ms);
  461. newel.SQLiteData = _ms.GetBuffer();
  462. }
  463. }
  464. return null;
  465. }
  466. public void Dispose()
  467. {
  468. Disconnect();
  469. }
  470. }
  471. }