Flask backend with Vue 3 frontend for shop floor machine management. Includes database schema export for MySQL shopdb_flask database. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
253 lines
7.5 KiB
Python
253 lines
7.5 KiB
Python
"""Unified Machine model - combines equipment and PCs."""
|
|
|
|
from shopdb.extensions import db
|
|
from .base import BaseModel, SoftDeleteMixin, AuditMixin
|
|
|
|
|
|
class MachineType(BaseModel):
|
|
"""
|
|
Machine type classification.
|
|
Categories: Equipment, PC, Network, Printer
|
|
"""
|
|
__tablename__ = 'machinetypes'
|
|
|
|
machinetypeid = db.Column(db.Integer, primary_key=True)
|
|
machinetype = db.Column(db.String(100), unique=True, nullable=False)
|
|
category = db.Column(
|
|
db.String(50),
|
|
nullable=False,
|
|
default='Equipment',
|
|
comment='Equipment, PC, Network, or Printer'
|
|
)
|
|
description = db.Column(db.Text)
|
|
icon = db.Column(db.String(50), comment='Icon name for UI')
|
|
|
|
def __repr__(self):
|
|
return f"<MachineType {self.machinetype}>"
|
|
|
|
|
|
class MachineStatus(BaseModel):
|
|
"""Machine status options."""
|
|
__tablename__ = 'machinestatuses'
|
|
|
|
statusid = db.Column(db.Integer, primary_key=True)
|
|
status = db.Column(db.String(50), unique=True, nullable=False)
|
|
description = db.Column(db.Text)
|
|
color = db.Column(db.String(20), comment='CSS color for UI')
|
|
|
|
def __repr__(self):
|
|
return f"<MachineStatus {self.status}>"
|
|
|
|
|
|
class PCType(BaseModel):
|
|
"""
|
|
PC type classification for more specific PC categorization.
|
|
Examples: Shopfloor PC, Engineer Workstation, CMM PC, etc.
|
|
"""
|
|
__tablename__ = 'pctypes'
|
|
|
|
pctypeid = db.Column(db.Integer, primary_key=True)
|
|
pctype = db.Column(db.String(100), unique=True, nullable=False)
|
|
description = db.Column(db.Text)
|
|
|
|
def __repr__(self):
|
|
return f"<PCType {self.pctype}>"
|
|
|
|
|
|
class Machine(BaseModel, SoftDeleteMixin, AuditMixin):
|
|
"""
|
|
Unified machine model for all asset types.
|
|
|
|
Machine types can be:
|
|
- CNC machines, CMMs, EDMs, etc. (manufacturing equipment)
|
|
- PCs (shopfloor PCs, engineer workstations, etc.)
|
|
- Network devices (servers, switches, etc.) - if network_devices plugin not used
|
|
|
|
The machinetype.category field distinguishes between types.
|
|
"""
|
|
__tablename__ = 'machines'
|
|
|
|
machineid = db.Column(db.Integer, primary_key=True)
|
|
|
|
# Identification
|
|
machinenumber = db.Column(
|
|
db.String(50),
|
|
unique=True,
|
|
nullable=False,
|
|
index=True,
|
|
comment='Business identifier (e.g., CMM01, G5QX1GT3ESF)'
|
|
)
|
|
alias = db.Column(
|
|
db.String(100),
|
|
comment='Friendly name'
|
|
)
|
|
hostname = db.Column(
|
|
db.String(100),
|
|
index=True,
|
|
comment='Network hostname (for PCs)'
|
|
)
|
|
serialnumber = db.Column(
|
|
db.String(100),
|
|
index=True,
|
|
comment='Hardware serial number'
|
|
)
|
|
|
|
# Classification
|
|
machinetypeid = db.Column(
|
|
db.Integer,
|
|
db.ForeignKey('machinetypes.machinetypeid'),
|
|
nullable=False
|
|
)
|
|
pctypeid = db.Column(
|
|
db.Integer,
|
|
db.ForeignKey('pctypes.pctypeid'),
|
|
nullable=True,
|
|
comment='Set for PCs, NULL for equipment'
|
|
)
|
|
businessunitid = db.Column(
|
|
db.Integer,
|
|
db.ForeignKey('businessunits.businessunitid'),
|
|
nullable=True
|
|
)
|
|
modelnumberid = db.Column(
|
|
db.Integer,
|
|
db.ForeignKey('models.modelnumberid'),
|
|
nullable=True
|
|
)
|
|
vendorid = db.Column(
|
|
db.Integer,
|
|
db.ForeignKey('vendors.vendorid'),
|
|
nullable=True
|
|
)
|
|
|
|
# Status
|
|
statusid = db.Column(
|
|
db.Integer,
|
|
db.ForeignKey('machinestatuses.statusid'),
|
|
default=1,
|
|
comment='In Use, Spare, Retired, etc.'
|
|
)
|
|
|
|
# Location and mapping
|
|
locationid = db.Column(
|
|
db.Integer,
|
|
db.ForeignKey('locations.locationid'),
|
|
nullable=True
|
|
)
|
|
mapleft = db.Column(db.Integer, comment='X coordinate on floor map')
|
|
maptop = db.Column(db.Integer, comment='Y coordinate on floor map')
|
|
islocationonly = db.Column(
|
|
db.Boolean,
|
|
default=False,
|
|
comment='Virtual location marker (not actual machine)'
|
|
)
|
|
|
|
# PC-specific fields (nullable for non-PC machines)
|
|
osid = db.Column(
|
|
db.Integer,
|
|
db.ForeignKey('operatingsystems.osid'),
|
|
nullable=True
|
|
)
|
|
loggedinuser = db.Column(db.String(100), nullable=True)
|
|
lastreporteddate = db.Column(db.DateTime, nullable=True)
|
|
lastboottime = db.Column(db.DateTime, nullable=True)
|
|
|
|
# Features/flags
|
|
isvnc = db.Column(db.Boolean, default=False, comment='VNC remote access enabled')
|
|
iswinrm = db.Column(db.Boolean, default=False, comment='WinRM enabled')
|
|
isshopfloor = db.Column(db.Boolean, default=False, comment='Shopfloor PC')
|
|
requiresmanualconfig = db.Column(
|
|
db.Boolean,
|
|
default=False,
|
|
comment='Multi-PC machine needs manual configuration'
|
|
)
|
|
|
|
# Notes
|
|
notes = db.Column(db.Text, nullable=True)
|
|
|
|
# Relationships
|
|
machinetype = db.relationship('MachineType', backref='machines')
|
|
pctype = db.relationship('PCType', backref='machines')
|
|
businessunit = db.relationship('BusinessUnit', backref='machines')
|
|
model = db.relationship('Model', backref='machines')
|
|
vendor = db.relationship('Vendor', backref='machines')
|
|
status = db.relationship('MachineStatus', backref='machines')
|
|
location = db.relationship('Location', backref='machines')
|
|
operatingsystem = db.relationship('OperatingSystem', backref='machines')
|
|
|
|
# Communications (one-to-many)
|
|
communications = db.relationship(
|
|
'Communication',
|
|
backref='machine',
|
|
cascade='all, delete-orphan',
|
|
lazy='dynamic'
|
|
)
|
|
|
|
# Installed applications (for PCs)
|
|
installedapps = db.relationship(
|
|
'InstalledApp',
|
|
back_populates='machine',
|
|
cascade='all, delete-orphan',
|
|
lazy='dynamic'
|
|
)
|
|
|
|
# Indexes
|
|
__table_args__ = (
|
|
db.Index('idx_machine_type_bu', 'machinetypeid', 'businessunitid'),
|
|
db.Index('idx_machine_location', 'locationid'),
|
|
db.Index('idx_machine_active', 'isactive'),
|
|
db.Index('idx_machine_hostname', 'hostname'),
|
|
)
|
|
|
|
def __repr__(self):
|
|
return f"<Machine {self.machinenumber}>"
|
|
|
|
@property
|
|
def display_name(self):
|
|
"""Get display name (alias if set, otherwise machinenumber)."""
|
|
return self.alias or self.machinenumber
|
|
|
|
@property
|
|
def derived_machinetype(self):
|
|
"""Get machinetype from model (single source of truth)."""
|
|
if self.model and self.model.machinetype:
|
|
return self.model.machinetype
|
|
return None
|
|
|
|
@property
|
|
def is_pc(self):
|
|
"""Check if this machine is a PC type."""
|
|
mt = self.derived_machinetype
|
|
return mt.category == 'PC' if mt else False
|
|
|
|
@property
|
|
def is_equipment(self):
|
|
"""Check if this machine is equipment."""
|
|
mt = self.derived_machinetype
|
|
return mt.category == 'Equipment' if mt else False
|
|
|
|
@property
|
|
def is_network_device(self):
|
|
"""Check if this machine is a network device."""
|
|
mt = self.derived_machinetype
|
|
return mt.category == 'Network' if mt else False
|
|
|
|
@property
|
|
def is_printer(self):
|
|
"""Check if this machine is a printer."""
|
|
mt = self.derived_machinetype
|
|
return mt.category == 'Printer' if mt else False
|
|
|
|
@property
|
|
def primary_ip(self):
|
|
"""Get primary IP address from communications."""
|
|
comm = self.communications.filter_by(
|
|
isprimary=True,
|
|
comtypeid=1 # IP type
|
|
).first()
|
|
if comm:
|
|
return comm.ipaddress
|
|
# Fall back to any IP
|
|
comm = self.communications.filter_by(comtypeid=1).first()
|
|
return comm.ipaddress if comm else None
|