public async Task <IActionResult> UpdateControllerAsync(int id, [FromBody] ControllerX delta, CancellationToken ct)
        {
            if (id <= 0)
            {
                return(NotFound());
            }
            if (delta.ID != 0)
            {
                return(BadRequest($"Controller ID cannot be changed."));
            }

            if (delta.Name != null)
            {
                if (string.IsNullOrWhiteSpace(delta.Name))
                {
                    return(BadRequest($"Invalid controller name: [{delta.Name}]."));
                }
                delta.Name = delta.Name.Trim();
            }

            if (!string.IsNullOrEmpty(delta.m_ControllerTypeText))
            {
                if (!int.TryParse(delta.m_ControllerTypeText.Trim(), out int ctype))
                {
                    if (Enum.TryParse(delta.m_ControllerTypeText.Trim(), true, out KnownControllerTypes ktype))
                    {
                        ctype = (int)ktype;
                    }
                    else
                    {
                        return(BadRequest($"Invalid controller type: [{delta.m_ControllerTypeText}]."));
                    }
                }

                delta.BaseControllerType = ctype;
            }

            if (delta.Version != null)
            {
                if (string.IsNullOrWhiteSpace(delta.Version))
                {
                    return(BadRequest($"Invalid version: [{delta.Version}]."));
                }
                delta.Version = delta.Version.Trim();
            }

            if (delta.Model != null)
            {
                if (string.IsNullOrWhiteSpace(delta.Model))
                {
                    return(BadRequest($"Invalid machine model: [{delta.Model}]."));
                }
                delta.Model = delta.Model.Trim();
            }

            if (delta.IP != null)
            {
                if (string.IsNullOrWhiteSpace(delta.IP))
                {
                    return(BadRequest($"Invalid IP address: [{delta.IP}]."));
                }
                delta.IP = delta.IP.Trim();

                if (!IPorSerialPortRegex.IsMatch(delta.IP))
                {
                    return(BadRequest($"Invalid IP address: [{delta.IP}]."));
                }
            }

            var orgId = HttpContext.GetOrg();

            using (var db = new ConfigDB()) {
                var controller = await db.Controllers
                                 .Where(c => c.OrgId.Equals(orgId, StringComparison.OrdinalIgnoreCase))
                                 .SingleOrDefaultAsync(c => c.ID == id, ct)
                                 .ConfigureAwait(false);

                if (controller == null)
                {
                    return(NotFound());
                }

                if (delta.m_IsEnabled.HasValue)
                {
                    controller.IsEnabled = delta.IsEnabled.Value;
                }
                if (delta.Name != null)
                {
                    controller.Name = delta.Name;
                }
                if (!string.IsNullOrWhiteSpace(delta.m_ControllerTypeText))
                {
                    controller.Type = delta.BaseControllerType;
                }
                if (delta.Version != null)
                {
                    controller.Version = delta.Version;
                }
                if (delta.Model != null)
                {
                    controller.Model = delta.Model;
                }
                if (delta.IP != null)
                {
                    controller.IP = delta.IP;
                }

                delta.Modified = DateTime.Now;

                await db.SaveChangesAsync(ct).ConfigureAwait(false);

                return(Ok(new ControllerX(controller)));
            }
        }
        public async Task <IActionResult> AddControllerAsync([FromBody] ControllerX controller, CancellationToken ct)
        {
            if (string.IsNullOrWhiteSpace(controller.Name))
            {
                return(BadRequest($"Invalid controller name: [{controller.Name}]."));
            }
            controller.Name = controller.Name.Trim();

            var ctype = 0;

            if (!string.IsNullOrWhiteSpace(controller.m_ControllerTypeText))
            {
                if (!int.TryParse(controller.m_ControllerTypeText.Trim(), out ctype))
                {
                    if (Enum.TryParse(controller.m_ControllerTypeText.Trim(), true, out KnownControllerTypes ktype))
                    {
                        ctype = (int)ktype;
                    }
                    else
                    {
                        return(BadRequest($"Invalid controller type: [{controller.m_ControllerTypeText}]."));
                    }
                }
            }

            controller.BaseControllerType = ctype;

            controller.Version = string.IsNullOrWhiteSpace(controller.Version) ? "0.0.0" : controller.Version.Trim();

            controller.Model = string.IsNullOrWhiteSpace(controller.Model) ? "Unknown" : controller.Model.Trim();

            controller.IP = string.IsNullOrWhiteSpace(controller.IP) ? "1.1.1.1" : controller.IP.Trim();

            if (!IPorSerialPortRegex.IsMatch(controller.IP))
            {
                return(BadRequest($"Invalid IP address: [{controller.IP}]."));
            }

            controller.OrgId    = HttpContext.GetOrg();
            controller.Modified = null;

            using (var db = new ConfigDB()) {
                var cx = await db.Controllers
                         .SingleOrDefaultAsync(c => c.ID == controller.ID, ct)
                         .ConfigureAwait(false);

                if (cx != null)
                {
                    if (cx.OrgId.Equals(controller.OrgId, StringComparison.OrdinalIgnoreCase))
                    {
                        return(BadRequest($"Controller ID [{controller.ID}] already exists."));
                    }
                    return(BadRequest($"Invalid controller ID [{controller.ID}]."));
                }

                cx = controller.GetBase();
                db.Controllers.Add(cx);
                await db.SaveChangesAsync(ct).ConfigureAwait(false);

                return(Created($"controllers/{cx.ID}", new ControllerX(cx)));
            }
        }